diff --git a/.env.example b/.env.example index 4e9f0406..05707f5c 100644 --- a/.env.example +++ b/.env.example @@ -91,3 +91,5 @@ MEILISEARCH_MASTER_KEY= CACHE_KEY_AGENT_ALLOW=all_agent_allows CACHE_KEY_AGENT_DENY=all_agent_denies CHANNEL_NAME_SETTING=channel_setting +CHANNEL_NAME_MODEL_EVENT=channel_model_event +FORCE_SCHEME= diff --git a/app/Console/Commands/FireEvent.php b/app/Console/Commands/FireEvent.php index 5d2772ce..91073f26 100644 --- a/app/Console/Commands/FireEvent.php +++ b/app/Console/Commands/FireEvent.php @@ -6,6 +6,7 @@ use App\Events\NewsCreated; use App\Events\TorrentCreated; use App\Events\TorrentDeleted; use App\Events\TorrentUpdated; +use App\Events\UserCreated; use App\Events\UserDestroyed; use App\Events\UserDisabled; use App\Events\UserEnabled; @@ -31,15 +32,18 @@ class FireEvent extends Command * * @var string */ - protected $description = 'Fire a event, options: --name, --idKey --idKeyOld'; + protected $description = 'Fire an event, options: --name, --idKey --idKeyOld'; protected array $eventMaps = [ "torrent_created" => ['event' => TorrentCreated::class, 'model' => Torrent::class], "torrent_updated" => ['event' => TorrentUpdated::class, 'model' => Torrent::class], "torrent_deleted" => ['event' => TorrentDeleted::class, 'model' => Torrent::class], + + "user_created" => ['event' => UserCreated::class, 'model' => User::class], "user_destroyed" => ['event' => UserDestroyed::class, 'model' => User::class], "user_disabled" => ['event' => UserDisabled::class, 'model' => User::class], "user_enabled" => ['event' => UserEnabled::class, 'model' => User::class], + "news_created" => ['event' => NewsCreated::class, 'model' => News::class], ]; @@ -69,6 +73,7 @@ class FireEvent extends Command } $result = call_user_func_array([$eventName, "dispatch"], $params); $log .= ", success call dispatch, result: " . var_export($result, true); + publish_model_event($name, $model->id); } else { $log .= ", invalid argument to call, it should be instance of: " . Model::class; } diff --git a/app/Console/Commands/Test.php b/app/Console/Commands/Test.php index 1f65219c..dcc33d94 100644 --- a/app/Console/Commands/Test.php +++ b/app/Console/Commands/Test.php @@ -40,10 +40,12 @@ use App\Repositories\UserRepository; use Carbon\Carbon; use Filament\Notifications\Notification; use GeoIp2\Database\Reader; +use GuzzleHttp\Client; use Illuminate\Console\Command; use Illuminate\Encryption\Encrypter; use Illuminate\Support\Facades\DB; use Illuminate\Support\Facades\Hash; +use Illuminate\Support\Facades\Http; use Illuminate\Support\Facades\Redis; use Illuminate\Support\Facades\Storage; use Illuminate\Support\Str; @@ -59,6 +61,7 @@ use NexusPlugin\Permission\Models\Role; use NexusPlugin\PostLike\PostLikeRepository; use NexusPlugin\StickyPromotion\Models\StickyPromotion; use NexusPlugin\StickyPromotion\Models\StickyPromotionParticipator; +use NexusPlugin\Tracker\TrackerRepository; use NexusPlugin\Work\Models\RoleWork; use NexusPlugin\Work\WorkRepository; use PhpIP\IP; @@ -98,7 +101,8 @@ class Test extends Command */ public function handle() { - CleanupRepository::checkQueueFailedJobs(); + $result = \Nexus\Plugin\Plugin::listEnabled(); + dd($result); } } diff --git a/app/Console/Kernel.php b/app/Console/Kernel.php index 809698a6..1d61ed62 100644 --- a/app/Console/Kernel.php +++ b/app/Console/Kernel.php @@ -4,6 +4,9 @@ namespace App\Console; use App\Jobs\CheckCleanup; use App\Jobs\CheckQueueFailedJobs; +use App\Jobs\MaintainPluginState; +use App\Jobs\ManagePlugin; +use App\Utils\ThirdPartyJob; use Carbon\Carbon; use Illuminate\Console\Scheduling\Event; use Illuminate\Console\Scheduling\Schedule; @@ -41,6 +44,8 @@ class Kernel extends ConsoleKernel $schedule->command('meilisearch:import')->weeklyOn(1, "03:00")->withoutOverlapping(); $schedule->command('torrent:load_pieces_hash')->dailyAt("01:00")->withoutOverlapping(); $schedule->job(new CheckQueueFailedJobs())->everySixHours()->withoutOverlapping(); + $schedule->job(new ThirdPartyJob())->everyMinute()->withoutOverlapping(); + $schedule->job(new MaintainPluginState())->everyMinute()->withoutOverlapping(); $this->registerScheduleCleanup($schedule); } diff --git a/app/Events/UserCreated.php b/app/Events/UserCreated.php new file mode 100644 index 00000000..609e7242 --- /dev/null +++ b/app/Events/UserCreated.php @@ -0,0 +1,39 @@ +model = $model; + } + + /** + * Get the channels the event should broadcast on. + * + * @return \Illuminate\Broadcasting\Channel|array + */ + public function broadcastOn() + { + return new PrivateChannel('channel-name'); + } +} diff --git a/app/Filament/Pages/Dashboard.php b/app/Filament/Pages/Dashboard.php index 3fd98ef6..198034de 100644 --- a/app/Filament/Pages/Dashboard.php +++ b/app/Filament/Pages/Dashboard.php @@ -1,7 +1,32 @@ after(function () { do_log("cache_del: global_promotion_state"); NexusDB::cache_del(Setting::TORRENT_GLOBAL_STATE_CACHE_KEY); + do_log("publish_model_event: global_promotion_state_updated"); + publish_model_event("global_promotion_state_updated", 0); }), // Tables\Actions\DeleteAction::make(), ]) diff --git a/app/Filament/Resources/User/ExamUserResource/Pages/ViewExamUser.php b/app/Filament/Resources/User/ExamUserResource/Pages/ViewExamUser.php index 6a21caa9..9e81b931 100644 --- a/app/Filament/Resources/User/ExamUserResource/Pages/ViewExamUser.php +++ b/app/Filament/Resources/User/ExamUserResource/Pages/ViewExamUser.php @@ -117,9 +117,4 @@ class ViewExamUser extends ViewRecord Actions\DeleteAction::make(), ]; } - - private function getProgress() - { - - } } diff --git a/app/Http/Middleware/TrustProxies.php b/app/Http/Middleware/TrustProxies.php index 3391630e..00cfb6dc 100644 --- a/app/Http/Middleware/TrustProxies.php +++ b/app/Http/Middleware/TrustProxies.php @@ -12,7 +12,7 @@ class TrustProxies extends Middleware * * @var array|string|null */ - protected $proxies; + protected $proxies = ['*']; /** * The headers that should be used to detect proxies. diff --git a/app/Jobs/BuyTorrent.php b/app/Jobs/BuyTorrent.php new file mode 100644 index 00000000..258a221b --- /dev/null +++ b/app/Jobs/BuyTorrent.php @@ -0,0 +1,68 @@ +userId = $userId; + $this->torrentId = $torrentId; + } + + /** + * Execute the job. + * + * @return void + */ + public function handle() + { + $logPrefix = sprintf("user: %s, torrent: %s", $this->userId, $this->torrentId); + $torrentRep = new TorrentRepository(); + $userId = $this->userId; + $torrentId = $this->torrentId; + + $hasBuy = TorrentBuyLog::query() + ->where("uid", $userId) + ->where("torrent_id", $torrentId) + ->exists() + ; + if ($hasBuy) { + //标记购买成功 + do_log("$logPrefix, already bought"); + $torrentRep->addBuySuccessCache($userId, $torrentId); + return; + } + try { + $bonusRep = new BonusRepository(); + $bonusRep->consumeToBuyTorrent($this->userId, $this->torrentId); + //标记购买成功 + do_log("$logPrefix, buy torrent success"); + $torrentRep->addBuySuccessCache($userId, $torrentId); + } catch (\Throwable $throwable) { + //标记购买失败,缓存 3600 秒,这个时间内不能再次购买 + do_log("$logPrefix, buy torrent fail: " . $throwable->getMessage(), "error"); + $torrentRep->addBuyFailCache($userId, $torrentId); + } + } +} diff --git a/app/Jobs/CalculateUserSeedBonus.php b/app/Jobs/CalculateUserSeedBonus.php index b750d0a9..4d8bce1e 100644 --- a/app/Jobs/CalculateUserSeedBonus.php +++ b/app/Jobs/CalculateUserSeedBonus.php @@ -9,6 +9,7 @@ use Illuminate\Contracts\Queue\ShouldBeUnique; use Illuminate\Contracts\Queue\ShouldQueue; use Illuminate\Foundation\Bus\Dispatchable; use Illuminate\Queue\InteractsWithQueue; +use Illuminate\Queue\Middleware\WithoutOverlapping; use Illuminate\Queue\SerializesModels; use Nexus\Database\NexusDB; use Nexus\Nexus; @@ -55,6 +56,16 @@ class CalculateUserSeedBonus implements ShouldQueue public $timeout = 3600; + /** + * 获取任务时,应该通过的中间件。 + * + * @return array + */ + public function middleware() + { + return [new WithoutOverlapping($this->idRedisKey)]; + } + /** * Execute the job. * @@ -63,7 +74,11 @@ class CalculateUserSeedBonus implements ShouldQueue public function handle() { $beginTimestamp = time(); - $logPrefix = sprintf("[CLEANUP_CLI_CALCULATE_SEED_BONUS_HANDLE_JOB], commonRequestId: %s, beginUid: %s, endUid: %s", $this->requestId, $this->beginUid, $this->endUid); + $logPrefix = sprintf( + "[CLEANUP_CLI_CALCULATE_SEED_BONUS_HANDLE_JOB], commonRequestId: %s, beginUid: %s, endUid: %s, idStr: %s, idRedisKey: %s", + $this->requestId, $this->beginUid, $this->endUid, $this->idStr, $this->idRedisKey + ); + do_log("$logPrefix, job start ..."); $haremAdditionFactor = Setting::get('bonus.harem_addition'); $officialAdditionFactor = Setting::get('bonus.official_addition'); $donortimes_bonus = Setting::get('bonus.donortimes'); @@ -84,6 +99,8 @@ class CalculateUserSeedBonus implements ShouldQueue $logFile = getLogFile("seed-bonus-points"); do_log("$logPrefix, [GET_UID_REAL], count: " . count($results) . ", logFile: $logFile"); $fd = fopen($logFile, 'a'); + $seedPointsUpdates = $seedPointsPerHourUpdates = $seedBonusUpdates = []; + $logStr = ""; foreach ($results as $userInfo) { $uid = $userInfo['id']; @@ -92,7 +109,7 @@ class CalculateUserSeedBonus implements ShouldQueue $bonusLog = "[CLEANUP_CLI_CALCULATE_SEED_BONUS_HANDLE_USER], user: $uid, seedBonusResult: " . nexus_json_encode($seedBonusResult); $all_bonus = $seedBonusResult['seed_bonus']; $bonusLog .= ", all_bonus: $all_bonus"; - if ($isDonor) { + if ($isDonor && $donortimes_bonus != 0) { $all_bonus = $all_bonus * $donortimes_bonus; $bonusLog .= ", isDonor, donortimes_bonus: $donortimes_bonus, all_bonus: $all_bonus"; } @@ -112,13 +129,17 @@ class CalculateUserSeedBonus implements ShouldQueue $all_bonus += $medalAddition; $bonusLog .= ", medalAdditionFactor: {$seedBonusResult['medal_additional_factor']}, medalBonus: {$seedBonusResult['medal_bonus']}, medalAddition: $medalAddition, all_bonus: $all_bonus"; } + do_log($bonusLog); $dividend = 3600 / $autoclean_interval_one; $all_bonus = $all_bonus / $dividend; $seed_points = $seedBonusResult['seed_points'] / $dividend; - $updatedAt = now()->toDateTimeString(); - $sql = "update users set seed_points = ifnull(seed_points, 0) + $seed_points, seed_points_per_hour = {$seedBonusResult['seed_points']}, seedbonus = seedbonus + $all_bonus, seed_points_updated_at = '$updatedAt' where id = $uid limit 1"; - do_log("$bonusLog, query: $sql"); - NexusDB::statement($sql); +// $updatedAt = now()->toDateTimeString(); +// $sql = "update users set seed_points = ifnull(seed_points, 0) + $seed_points, seed_points_per_hour = {$seedBonusResult['seed_points']}, seedbonus = seedbonus + $all_bonus, seed_points_updated_at = '$updatedAt' where id = $uid limit 1"; +// do_log("$bonusLog, query: $sql"); +// NexusDB::statement($sql); + $seedPointsUpdates[] = sprintf("when %d then ifnull(seed_points, 0) + %f", $uid, $seed_points); + $seedPointsPerHourUpdates[] = sprintf("when %d then %f", $uid, $seedBonusResult['seed_points']); + $seedBonusUpdates[] = sprintf("when %d then seedbonus + %f", $uid, $all_bonus); if ($fd) { $log = sprintf( '%s|%s|%s|%s|%s|%s|%s|%s', @@ -126,16 +147,28 @@ class CalculateUserSeedBonus implements ShouldQueue $userInfo['seed_points'], number_format($seed_points, 1, '.', ''), number_format($userInfo['seed_points'] + $seed_points, 1, '.', ''), $userInfo['seedbonus'], number_format($all_bonus, 1, '.', ''), number_format($userInfo['seedbonus'] + $all_bonus, 1, '.', '') ); - fwrite($fd, $log . PHP_EOL); +// fwrite($fd, $log . PHP_EOL); + $logStr .= $log . PHP_EOL; } else { do_log("logFile: $logFile is not writeable!", 'error'); } } + $nowStr = now()->toDateTimeString(); + $sql = sprintf( + "update users set seed_points = case id %s end, seed_points_per_hour = case id %s end, seedbonus = case id %s end, seed_points_updated_at = '%s' where id in (%s)", + implode(" ", $seedPointsUpdates), implode(" ", $seedPointsPerHourUpdates), implode(" ", $seedBonusUpdates), $nowStr, $idStr + ); + $result = NexusDB::statement($sql); if ($delIdRedisKey) { NexusDB::cache_del($this->idRedisKey); } + fwrite($fd, $logStr); $costTime = time() - $beginTimestamp; - do_log("$logPrefix, [DONE], cost time: $costTime seconds"); + do_log(sprintf( + "$logPrefix, [DONE], update user count: %s, result: %s, cost time: %s seconds", + count($seedPointsUpdates), var_export($result, true), $costTime + )); + do_log("$logPrefix, sql: $sql", "debug"); } /** diff --git a/app/Jobs/LoadTorrentBoughtUsers.php b/app/Jobs/LoadTorrentBoughtUsers.php index a75a40a1..90e05d99 100644 --- a/app/Jobs/LoadTorrentBoughtUsers.php +++ b/app/Jobs/LoadTorrentBoughtUsers.php @@ -16,6 +16,10 @@ class LoadTorrentBoughtUsers implements ShouldQueue private int $torrentId; + public $tries = 1; + + public $timeout = 1800; + /** * Create a new job instance. * diff --git a/app/Jobs/MaintainPluginState.php b/app/Jobs/MaintainPluginState.php new file mode 100644 index 00000000..f1d84bc0 --- /dev/null +++ b/app/Jobs/MaintainPluginState.php @@ -0,0 +1,44 @@ +del($key); + $nowStr = now()->toDateTimeString(); + foreach ($enabled as $name => $value) { + NexusDB::redis()->hSet($key, $name, $nowStr); + } + do_log("$key: " . nexus_json_encode($enabled)); + } +} diff --git a/app/Jobs/UpdateTorrentSeedersEtc.php b/app/Jobs/UpdateTorrentSeedersEtc.php index e85a4dd8..6068bc78 100644 --- a/app/Jobs/UpdateTorrentSeedersEtc.php +++ b/app/Jobs/UpdateTorrentSeedersEtc.php @@ -10,6 +10,7 @@ use Illuminate\Contracts\Queue\ShouldBeUnique; use Illuminate\Contracts\Queue\ShouldQueue; use Illuminate\Foundation\Bus\Dispatchable; use Illuminate\Queue\InteractsWithQueue; +use Illuminate\Queue\Middleware\WithoutOverlapping; use Illuminate\Queue\SerializesModels; use Nexus\Database\NexusDB; @@ -55,6 +56,16 @@ class UpdateTorrentSeedersEtc implements ShouldQueue public $timeout = 1800; + /** + * 获取任务时,应该通过的中间件。 + * + * @return array + */ + public function middleware() + { + return [new WithoutOverlapping($this->idRedisKey)]; + } + /** * Execute the job. * @@ -63,7 +74,11 @@ class UpdateTorrentSeedersEtc implements ShouldQueue public function handle() { $beginTimestamp = time(); - $logPrefix = sprintf("[CLEANUP_CLI_UPDATE_TORRENT_SEEDERS_ETC_HANDLE_JOB], commonRequestId: %s, beginTorrentId: %s, endTorrentId: %s", $this->requestId, $this->beginTorrentId, $this->endTorrentId); + $logPrefix = sprintf( + "[CLEANUP_CLI_UPDATE_TORRENT_SEEDERS_ETC_HANDLE_JOB], commonRequestId: %s, beginTorrentId: %s, endTorrentId: %s, idStr: %s, idRedisKey: %s", + $this->requestId, $this->beginTorrentId, $this->endTorrentId, $this->idStr, $this->idRedisKey + ); + do_log("$logPrefix, job start ..."); $idStr = $this->idStr; $delIdRedisKey = false; @@ -76,44 +91,51 @@ class UpdateTorrentSeedersEtc implements ShouldQueue return; } $torrentIdArr = explode(",", $idStr); - foreach ($torrentIdArr as $torrentId) { - if ($torrentId <= 0) { - continue; - } - $peerResult = NexusDB::table('peers') - ->where('torrent', $torrentId) - ->selectRaw("count(*) as count, seeder") - ->groupBy('seeder') - ->get() - ; - $commentResult = NexusDB::table('comments') - ->where('torrent',$torrentId) - ->selectRaw("count(*) as count") - ->first() - ; - $update = [ - 'comments' => $commentResult && $commentResult->count !== null ? $commentResult->count : 0, - 'seeders' => 0, - 'leechers' => 0, - ]; - foreach ($peerResult as $item) { - if ($item->seeder == 'yes') { - $update['seeders'] = $item->count; - } elseif ($item->seeder == 'no') { - $update['leechers'] = $item->count; - } - } - NexusDB::table('torrents')->where('id', $torrentId)->update($update); - do_log("[CLEANUP_CLI_UPDATE_TORRENT_SEEDERS_ETC_HANDLE_TORRENT], [SUCCESS]: $torrentId => " . json_encode($update)); + //批量取,简单化 + $torrents = array(); +// $res = sql_query("SELECT torrent, seeder, COUNT(*) AS c FROM peers GROUP BY torrent, seeder where torrent in ($idStr)"); + $res = NexusDB::table("peers") + ->selectRaw("torrent, seeder, COUNT(*) AS c") + ->whereRaw("torrent in ($idStr)") + ->groupBy(['torrent', 'seeder']) + ->get(); + foreach ($res as $row) { + if ($row->seeder == "yes") + $key = "seeders"; + else + $key = "leechers"; + $torrents[$row->torrent][$key] = $row->c; } + +// $res = sql_query("SELECT torrent, COUNT(*) AS c FROM comments GROUP BY torrent where torrent in ($idStr)"); + $res = NexusDB::table("comments") + ->selectRaw("torrent, COUNT(*) AS c") + ->whereRaw("torrent in ($idStr)") + ->groupBy(['torrent']) + ->get(); + foreach ($res as $row) { + $torrents[$row->torrent]["comments"] = $row->c; + } + $seedersUpdates = $leechersUpdates = $commentsUpdates = []; + foreach ($torrentIdArr as $id) { + $seedersUpdates[] = sprintf("when %d then %d", $id, $torrents[$id]["seeders"] ?? 0); + $leechersUpdates[] = sprintf("when %d then %d", $id, $torrents[$id]["leechers"] ?? 0); + $commentsUpdates[] = sprintf("when %d then %d", $id, $torrents[$id]["comments"] ?? 0); + } + $sql = sprintf( + "update torrents set seeders = case id %s end, leechers = case id %s end, comments = case id %s end where id in (%s)", + implode(" ", $seedersUpdates), implode(" ", $leechersUpdates), implode(" ", $commentsUpdates), $idStr + ); + $result = NexusDB::statement($sql); if ($delIdRedisKey) { NexusDB::cache_del($this->idRedisKey); } $costTime = time() - $beginTimestamp; do_log(sprintf( - "$logPrefix, [DONE], update torrent count: %s, cost time: %s seconds", - count($torrentIdArr), $costTime + "$logPrefix, [DONE], update torrent count: %s, result: %s, cost time: %s seconds", + count($torrentIdArr), var_export($result, true), $costTime )); + do_log("$logPrefix, sql: $sql", "debug"); } /** diff --git a/app/Jobs/UpdateUserSeedingLeechingTime.php b/app/Jobs/UpdateUserSeedingLeechingTime.php index d12ba451..cc3cf5be 100644 --- a/app/Jobs/UpdateUserSeedingLeechingTime.php +++ b/app/Jobs/UpdateUserSeedingLeechingTime.php @@ -10,6 +10,7 @@ use Illuminate\Contracts\Queue\ShouldBeUnique; use Illuminate\Contracts\Queue\ShouldQueue; use Illuminate\Foundation\Bus\Dispatchable; use Illuminate\Queue\InteractsWithQueue; +use Illuminate\Queue\Middleware\WithoutOverlapping; use Illuminate\Queue\SerializesModels; use Nexus\Database\NexusDB; @@ -55,6 +56,16 @@ class UpdateUserSeedingLeechingTime implements ShouldQueue public $timeout = 3600; + /** + * 获取任务时,应该通过的中间件。 + * + * @return array + */ + public function middleware() + { + return [new WithoutOverlapping($this->idRedisKey)]; + } + /** * Execute the job. * @@ -63,9 +74,12 @@ class UpdateUserSeedingLeechingTime implements ShouldQueue public function handle() { $beginTimestamp = time(); - $logPrefix = sprintf("[CLEANUP_CLI_UPDATE_SEEDING_LEECHING_TIME_HANDLE_JOB], commonRequestId: %s, beginUid: %s, endUid: %s", $this->requestId, $this->beginUid, $this->endUid); + $logPrefix = sprintf( + "[CLEANUP_CLI_UPDATE_SEEDING_LEECHING_TIME_HANDLE_JOB], commonRequestId: %s, beginUid: %s, endUid: %s, idStr: %s, idRedisKey: %s", + $this->requestId, $this->beginUid, $this->endUid, $this->idStr, $this->idRedisKey, + ); + do_log("$logPrefix, job start ..."); - $count = 0; $idStr = $this->idStr; $delIdRedisKey = false; if (empty($idStr) && !empty($this->idRedisKey)) { @@ -76,33 +90,35 @@ class UpdateUserSeedingLeechingTime implements ShouldQueue do_log("$logPrefix, no idStr or idRedisKey", "error"); return; } - $uidArr = explode(",", $idStr); - foreach ($uidArr as $uid) { - if ($uid <= 0) { - continue; - } - $sumInfo = NexusDB::table('snatched') - ->selectRaw('sum(seedtime) as seedtime_sum, sum(leechtime) as leechtime_sum') - ->where('userid', $uid) - ->first(); - if ($sumInfo && $sumInfo->seedtime_sum !== null) { - $update = [ - 'seedtime' => $sumInfo->seedtime_sum ?? 0, - 'leechtime' => $sumInfo->leechtime_sum ?? 0, - 'seed_time_updated_at' => Carbon::now()->toDateTimeString(), - ]; - NexusDB::table('users') - ->where('id', $uid) - ->update($update); - do_log("[CLEANUP_CLI_UPDATE_SEEDING_LEECHING_TIME_HANDLE_USER], [SUCCESS]: $uid => " . json_encode($update)); - $count++; - } + //批量取,简单化 +// $res = sql_query("select userid, sum(seedtime) as seedtime_sum, sum(leechtime) as leechtime_sum from snatched group by userid where userid in ($idStr)"); + $res = NexusDB::table("snatched") + ->selectRaw("userid, sum(seedtime) as seedtime_sum, sum(leechtime) as leechtime_sum") + ->whereRaw("userid in ($idStr)") + ->groupBy("userid") + ->get(); + $seedtimeUpdates = $leechTimeUpdates = []; + $nowStr = now()->toDateTimeString(); + $count = 0; + foreach ($res as $row) { + $count++; + $seedtimeUpdates = sprintf("when %d then %d", $row->userid, $row->seedtime_sum ?? 0); + $leechTimeUpdates = sprintf("when %d then %d", $row->userid, $row->leechtime_sum ?? 0); } + $sql = sprintf( + "update users set seedtime = case id %s end, leechtime = case id %s end, seed_time_updated_at = '%s' where id in (%s)", + implode(" ", $seedtimeUpdates), implode(" ", $leechTimeUpdates), $nowStr, $idStr + ); + $result = NexusDB::statement($sql); if ($delIdRedisKey) { NexusDB::cache_del($this->idRedisKey); } $costTime = time() - $beginTimestamp; - do_log("$logPrefix, [DONE], user total count: " . count($uidArr) . ", success update count: $count, cost time: $costTime seconds"); + do_log(sprintf( + "$logPrefix, [DONE], update user count: %s, result: %s, cost time: %s seconds", + $count, var_export($result, true), $costTime + )); + do_log("$logPrefix, sql: $sql", "debug"); } /** diff --git a/app/Models/User.php b/app/Models/User.php index f3ad2362..163affdf 100644 --- a/app/Models/User.php +++ b/app/Models/User.php @@ -184,7 +184,8 @@ class User extends Authenticatable implements FilamentUser, HasName protected $fillable = [ 'username', 'email', 'passhash', 'secret', 'stylesheet', 'editsecret', 'added', 'modcomment', 'enabled', 'status', 'leechwarn', 'leechwarnuntil', 'page', 'class', 'uploaded', 'downloaded', 'clientselect', 'showclienterror', 'last_home', - 'seedbonus', 'bonuscomment', 'downloadpos', 'vip_added', 'vip_until', 'title', 'invites', 'attendance_card', 'seed_points_per_hour' + 'seedbonus', 'bonuscomment', 'downloadpos', 'vip_added', 'vip_until', 'title', 'invites', 'attendance_card', + 'seed_points_per_hour', 'passkey', ]; /** diff --git a/app/Providers/AppServiceProvider.php b/app/Providers/AppServiceProvider.php index 1957c680..66bed42f 100644 --- a/app/Providers/AppServiceProvider.php +++ b/app/Providers/AppServiceProvider.php @@ -6,6 +6,7 @@ use App\Http\Middleware\Locale; use Carbon\Carbon; use Illuminate\Support\Facades\App; use Illuminate\Support\Facades\DB; +use Illuminate\Support\Facades\URL; use Illuminate\Support\Facades\View; use Illuminate\Support\ServiceProvider; use Illuminate\Http\Resources\Json\JsonResource; @@ -37,8 +38,11 @@ class AppServiceProvider extends ServiceProvider { global $plugin; $plugin->start(); -// JsonResource::withoutWrapping(); DB::connection(config('database.default'))->enableQueryLog(); + $forceScheme = strtolower(env('FORCE_SCHEME')); + if (env('APP_ENV') == "production" && in_array($forceScheme, ['https', 'http'])) { + URL::forceScheme($forceScheme); + } Filament::serving(function () { Filament::registerNavigationGroups([ diff --git a/app/Repositories/CleanupRepository.php b/app/Repositories/CleanupRepository.php index d7e8659a..d954ff45 100644 --- a/app/Repositories/CleanupRepository.php +++ b/app/Repositories/CleanupRepository.php @@ -169,7 +169,10 @@ for k, v in pairs(batchList) do local isBatchKeyNew = false if batchKey == false then batchKey = v .. ":" .. ARGV[4] - redis.call("SET", v, batchKey, "EX", ARGV[5]) + redis.call("SET", v, batchKey) + if (k > 1) then + redis.call("EXPIRE", v, ARGV[5]) + end isBatchKeyNew = true end local hashKey diff --git a/app/Repositories/DashboardRepository.php b/app/Repositories/DashboardRepository.php index 550ccb57..16720fdc 100644 --- a/app/Repositories/DashboardRepository.php +++ b/app/Repositories/DashboardRepository.php @@ -9,6 +9,7 @@ use App\Models\User; use Carbon\Carbon; use Filament\Facades\Filament; use Illuminate\Support\Facades\DB; +use Nexus\Database\NexusDB; class DashboardRepository extends BaseRepository { @@ -51,18 +52,26 @@ class DashboardRepository extends BaseRepository 'text' => nexus_trans("dashboard.system_info.$name"), 'value' => DB::select(DB::raw('select version() as info'))[0]->info, ]; - $name = 'os'; +// $name = 'os'; +// $result[$name] = [ +// 'name' => $name, +// 'text' => nexus_trans("dashboard.system_info.$name"), +// 'value' => PHP_OS, +// ]; + $name = 'redis_version'; $result[$name] = [ 'name' => $name, 'text' => nexus_trans("dashboard.system_info.$name"), - 'value' => PHP_OS, + 'value' => NexusDB::redis()->info()['redis_version'], ]; + $name = 'server_software'; $result[$name] = [ 'name' => $name, 'text' => nexus_trans("dashboard.system_info.$name"), 'value' => $_SERVER['SERVER_SOFTWARE'] ?? '', ]; + $name = 'load_average'; $result[$name] = [ 'name' => $name, diff --git a/app/Repositories/ExamRepository.php b/app/Repositories/ExamRepository.php index 35e3cb96..61022bb5 100644 --- a/app/Repositories/ExamRepository.php +++ b/app/Repositories/ExamRepository.php @@ -1327,32 +1327,4 @@ class ExamRepository extends BaseRepository return $result; } - public function fixIndexUploadTorrentCount() - { - $page = 1; - $size = 2000; - $examUserTable = (new ExamUser())->getTable(); - $examProgressTable = (new ExamProgress())->getTable(); - while (true) { - $offset = ($page - 1)*$size; - $list = NexusDB::table($examProgressTable) - ->select("$examProgressTable.*, $examUserTable.created_at as exam_created_at") - ->join($examUserTable, "$examProgressTable.exam_user_id", "=", "$examUserTable.id", "left") - ->where("$examUserTable.status", ExamUser::STATUS_NORMAL) - ->where("$examProgressTable.index", Exam::INDEX_UPLOAD_TORRENT_COUNT) - ->limit($size) - ->offset($offset) - ->get() - ; - if ($list->count() == 0) { - do_log("page: $page, offset: $offset, no more data..."); - return; - } - foreach ($list as $item) { - - } - - } - } - } diff --git a/app/Repositories/SeedBoxRepository.php b/app/Repositories/SeedBoxRepository.php index 05083dc9..052c6154 100644 --- a/app/Repositories/SeedBoxRepository.php +++ b/app/Repositories/SeedBoxRepository.php @@ -34,6 +34,7 @@ class SeedBoxRepository extends BaseRepository $params = $this->formatParams($params); $seedBoxRecord = SeedBoxRecord::query()->create($params); $this->clearCache(); + publish_model_event("seed_box_record_created", $seedBoxRecord->id); return $seedBoxRecord; } @@ -85,6 +86,7 @@ class SeedBoxRepository extends BaseRepository $params = $this->formatParams($params); $model->update($params); $this->clearCache(); + publish_model_event("seed_box_record_updated", $id); return $model; } diff --git a/app/Repositories/ToolRepository.php b/app/Repositories/ToolRepository.php index 4f339c0b..6cce94a5 100644 --- a/app/Repositories/ToolRepository.php +++ b/app/Repositories/ToolRepository.php @@ -1,6 +1,7 @@ whereIn("id", $receiverUidArr)->get(User::$commonFields); + foreach ($users as $user) { + $locale = $user->locale; + $subject = nexus_trans($subjectTransKey, $subjectTransContext, $locale); + $msg = nexus_trans($msgTransKey, $msgTransContext, $locale); + $result = $this->sendMail($user->email, $subject, $msg); + do_log(sprintf("send msg: %s result: %s", $msg, var_export($result, true)), $result ? "info" : "error"); + } + } + } } diff --git a/app/Repositories/TorrentRepository.php b/app/Repositories/TorrentRepository.php index 033f68d7..64507426 100644 --- a/app/Repositories/TorrentRepository.php +++ b/app/Repositories/TorrentRepository.php @@ -36,13 +36,23 @@ use Illuminate\Support\Str; use Nexus\Database\NexusDB; use Nexus\Imdb\Imdb; use Rhilip\Bencode\Bencode; +use Firebase\JWT\JWT; +use Firebase\JWT\Key; class TorrentRepository extends BaseRepository { - const BOUGHT_USER_CACHE_KEY_PREFIX = "torrent_purchasers:"; + const BOUGHT_USER_CACHE_KEY_PREFIX = "torrent_purchasers"; + + const BUY_FAIL_CACHE_KEY_PREFIX = "torrent_purchase_fails"; const PIECES_HASH_CACHE_KEY = "torrent_pieces_hash"; + const BUY_STATUS_SUCCESS = 0; + const BUY_STATUS_NOT_YET = -1; + const BUY_STATUS_UNKNOWN = -2; + + + /** * fetch torrent list * @@ -334,13 +344,23 @@ class TorrentRepository extends BaseRepository public function encryptDownHash($id, $user): string { $key = $this->getEncryptDownHashKey($user); - return (new Hashids($key))->encode($id); + $payload = [ + 'id' => $id, + 'exp' => time() + 3600 + ]; + return JWT::encode($payload, $key, 'HS256'); } public function decryptDownHash($downHash, $user) { $key = $this->getEncryptDownHashKey($user); - return (new Hashids($key))->decode($downHash); + try { + $decoded = JWT::decode($downHash, new Key($key, 'HS256')); + return [$decoded->id]; + } catch (\Exception $e) { + do_log("Invalid down hash: $downHash, " . $e->getMessage(), "error"); + return ''; + } } private function getEncryptDownHashKey($user) @@ -752,15 +772,94 @@ HTML; return $total; } - public function addBoughtUserToCache($torrentId, $uid) + /** + * 购买成功缓存,保存为 hash,一个种子一个 hash,永久有效 + * @param $uid + * @param $torrentId + * @return void + * @throws \RedisException + */ + public function addBuySuccessCache($uid, $torrentId): void { NexusDB::redis()->hSet($this->getBoughtUserCacheKey($torrentId), $uid, 1); } - - private function getBoughtUserCacheKey($torrentId): string + public function hasBuySuccessCache($uid, $torrentId): bool { - return self::BOUGHT_USER_CACHE_KEY_PREFIX . $torrentId; + return NexusDB::redis()->hGet($this->getBoughtUserCacheKey($torrentId), $uid) == 1; + } + + /** + * 获取购买种子的缓存状态 + * + * @param $uid + * @param $torrentId + * @return int + */ + public function getBuyStatus($uid, $torrentId): int + { + //查询是否已经购买 + if ($this->hasBuySuccessCache($uid, $torrentId)) { + return self::BUY_STATUS_SUCCESS; + } + //是否购买失败过 + $buyFailCount = $this->getBuyFailCache($uid, $torrentId); + if ($buyFailCount > 0) { + //根据失败次数,禁用下载权限并做提示等 + return $buyFailCount; + } + //不是成功或失败,直接返回未知 + return self::BUY_STATUS_UNKNOWN; + } + + /** + * 添加购买失败缓存, 结果累加 + * @param $uid + * @param $torrentId + * @return void + * @throws \RedisException + */ + public function addBuyFailCache($uid, $torrentId): void + { + $key = $this->getBuyFailCacheKey($uid, $torrentId); + $result = NexusDB::redis()->incr($key); + if ($result == 1) { + NexusDB::redis()->expire($key, 3600); + } + } + + /** + * 获取失败缓存 ,结果是失败的次数 + * + * @param $uid + * @param $torrentId + * @return int + * @throws \RedisException + */ + public function getBuyFailCache($uid, $torrentId): int + { + return intval(NexusDB::redis()->get($this->getBuyFailCacheKey($uid, $torrentId))); + } + + /** + * 购买成功缓存 key + * @param $torrentId + * @return string + */ + public function getBoughtUserCacheKey($torrentId): string + { + return sprintf("%s:%s", self::BOUGHT_USER_CACHE_KEY_PREFIX, $torrentId); + } + + /** + * 购买失败缓存 key + * @param int $userId + * @param int $torrentId + * @return string + */ + public function getBuyFailCacheKey(int $userId, int $torrentId): string + { + return sprintf("%s:%s:%s", self::BUY_FAIL_CACHE_KEY_PREFIX, $userId, $torrentId); } public function addPiecesHashCache(int $torrentId, string $piecesHash): bool|int|\Redis diff --git a/app/Repositories/UserRepository.php b/app/Repositories/UserRepository.php index d8c15f1e..74a5493a 100644 --- a/app/Repositories/UserRepository.php +++ b/app/Repositories/UserRepository.php @@ -132,7 +132,8 @@ class UserRepository extends BaseRepository 'stylesheet' => $setting['defstylesheet'], 'added' => now()->toDateTimeString(), 'status' => User::STATUS_CONFIRMED, - 'class' => $class + 'class' => $class, + 'passkey' => md5($username.date("Y-m-d H:i:s").$passhash) ]; $user = new User($data); if (!empty($params['id'])) { @@ -143,7 +144,7 @@ class UserRepository extends BaseRepository $user->id = $params['id']; } $user->save(); - + fire_event("user_created", $user); return $user; } diff --git a/app/Utils/MsgAlert.php b/app/Utils/MsgAlert.php new file mode 100644 index 00000000..8963ba29 --- /dev/null +++ b/app/Utils/MsgAlert.php @@ -0,0 +1,85 @@ +lRange($this->getListKey(), 0, 10); + if (!empty($result)) { + $nowTimestamp = time(); + $valid = []; + foreach ($result as $item) { + $arr = json_decode($item, true); + if (is_array($arr) && $arr['deadline'] > $nowTimestamp) { + $valid[$arr['name']] = $arr; + } else { + $redis->lRem($this->getListKey(), $item, 0); + } + } + self::$alerts = $valid; + } + } + + private function __clone() + { + + } + + public static function getInstance(): MsgAlert + { + if (isset(self::$instance)) { + return self::$instance; + } + return self::$instance = new self; + } + + + + public function add(string $name, int $deadline, string $text, string $url = "", string $color = "red"): void + { + if (!isset(self::$alerts[$name])) { + $params = compact('name', 'deadline', 'text', 'url', 'color'); + self::$alerts[$name] = $params; + NexusDB::redis()->rPush($this->getListKey(), json_encode($params)); + } + } + + private function getListKey(): string + { + return sprintf("%s:%s", $this->redisKeyPrefix, get_user_id()); + } + + + public static function render(): void + { + $nowTimestamp = time(); + foreach (self::$alerts as $item) { + if ($item['deadline'] > $nowTimestamp) { + msgalert($item['url'] ?: '', $item['text'], $item['color'] ?: 'red'); + } + } + } + + public function remove($name): void + { + foreach (self::$alerts as $item) { + if ($item['name'] = $name) { + unset(self::$alerts[$name]); + NexusDB::redis()->lRem($this->getListKey(), json_encode($item)); + } + } + } + + + +} diff --git a/app/Utils/ThirdPartyJob.php b/app/Utils/ThirdPartyJob.php new file mode 100644 index 00000000..96a8b4a1 --- /dev/null +++ b/app/Utils/ThirdPartyJob.php @@ -0,0 +1,70 @@ +get()) { + do_log("can not get lock: $lockName, return ..."); + return; + } + $list = NexusDB::redis()->lRange(self::$queueKey, 0, self::$size); + $successCount = 0; + foreach ($list as $item) { + $data = json_decode($item, true); + if (!empty($data['name'])) { + $successCount++; + match ($data['name']) { + self::JOB_BUY_TORRENT => self::enqueueJobBuyTorrent($data), + default => throw new InvalidArgumentException("invalid name: {$data['name']}") + }; + } else { + do_log(sprintf("%s no name, skip", $item), "error"); + } + NexusDB::redis()->lRem(self::$queueKey, $item); + } + do_log(sprintf("success dispatch %s jobs", $successCount)); + $lock->release(); + } + + public static function addBuyTorrent(int $userId, int $torrentId): void + { + $key = sprintf("%s:%s_%s", self::$queueKey, $userId, $torrentId); + if (NexusDB::redis()->set($key, now()->toDateTimeString(), ['nx', 'ex' => 3600])) { + $value = [ + 'name' => self::JOB_BUY_TORRENT, + 'userId' => $userId, + 'torrentId' => $torrentId, + ]; + NexusDB::redis()->rPush(self::$queueKey, json_encode($value)); + do_log("success addBuyTorrent: $key", "debug"); + } else { + do_log("no need to addBuyTorrent: $key", "debug"); + } + } + + private static function enqueueJobBuyTorrent(array $params): void + { + if (!empty($params['userId']) && !empty($params['torrentId'])) { + $job = new BuyTorrent($params['userId'], $params['torrentId']); + Queue::push($job); + } else { + do_log("no userId or torrentId: " . json_encode($params), "error"); + } + } +} diff --git a/composer.json b/composer.json index c643f3b1..a02457fa 100644 --- a/composer.json +++ b/composer.json @@ -42,7 +42,7 @@ "hashids/hashids": "^4.1", "imdbphp/imdbphp": "^7.0", "irazasyed/telegram-bot-sdk": "^3.11", - "laravel/framework": "9.52.4", + "laravel/framework": "v9.52.17", "laravel/octane": "^1.2", "laravel/passport": "^11.10", "laravel/sanctum": "^2.10", diff --git a/composer.lock b/composer.lock index 308e76fa..cc40eedd 100644 --- a/composer.lock +++ b/composer.lock @@ -4,7 +4,7 @@ "Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies", "This file is @generated automatically" ], - "content-hash": "2b89de67bf5d41d008100e25d21303b0", + "content-hash": "546ab4accfd6fa5ec14eeadfbc14e573", "packages": [ { "name": "akaunting/laravel-money", @@ -3189,16 +3189,16 @@ }, { "name": "laravel/framework", - "version": "v9.52.4", + "version": "v9.52.17", "source": { "type": "git", "url": "https://github.com/laravel/framework.git", - "reference": "9239128cfb4d22afefb64060dfecf53e82987267" + "reference": "a069cf17e4943fb88d2a91c92690004fb3236dab" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/laravel/framework/zipball/9239128cfb4d22afefb64060dfecf53e82987267", - "reference": "9239128cfb4d22afefb64060dfecf53e82987267", + "url": "https://api.github.com/repos/laravel/framework/zipball/a069cf17e4943fb88d2a91c92690004fb3236dab", + "reference": "a069cf17e4943fb88d2a91c92690004fb3236dab", "shasum": "" }, "require": { @@ -3295,7 +3295,7 @@ "league/flysystem-read-only": "^3.3", "league/flysystem-sftp-v3": "^3.0", "mockery/mockery": "^1.5.1", - "orchestra/testbench-core": "^7.16", + "orchestra/testbench-core": "^7.24", "pda/pheanstalk": "^4.0", "phpstan/phpdoc-parser": "^1.15", "phpstan/phpstan": "^1.4.7", @@ -3383,7 +3383,7 @@ "issues": "https://github.com/laravel/framework/issues", "source": "https://github.com/laravel/framework" }, - "time": "2023-02-22T14:38:06+00:00" + "time": "2024-11-12T15:39:14+00:00" }, { "name": "laravel/octane", @@ -13996,6 +13996,6 @@ "ext-xml": "*", "ext-zend-opcache": "*" }, - "platform-dev": [], + "platform-dev": {}, "plugin-api-version": "2.6.0" } diff --git a/database/migrations/2024_10_13_035900_change_torrents_table_info_hash_nullable.php b/database/migrations/2024_10_13_035900_change_torrents_table_info_hash_nullable.php new file mode 100644 index 00000000..1906a12f --- /dev/null +++ b/database/migrations/2024_10_13_035900_change_torrents_table_info_hash_nullable.php @@ -0,0 +1,29 @@ +." + tagRemove + ".value = ' " + tagRemove + " '"); + eval("document.." + tagRemove + ".value = ' " + tagRemove.toUpperCase() + " '"); eval(tagRemove + "_open = 0"); } else { doInsert("[/"+tagRemove+"]", "", false); @@ -2798,6 +2798,8 @@ if ($msgalert) $text = $lang_functions['text_you_have'].$unread.$lang_functions['text_new_message'] . add_s($unread) . $lang_functions['text_click_here_to_read']; msgalert("messages.php",$text, "red"); } + \App\Utils\MsgAlert::getInstance()->render(); + /* $pending_invitee = $Cache->get_value('user_'.$CURUSER["id"].'_pending_invitee_count'); if ($pending_invitee == ""){ @@ -3280,7 +3282,7 @@ function commenttable($rows, $type, $parent_id, $review = false) $dt = sqlesc(date("Y-m-d H:i:s",(TIMENOW - $secs))); // calculate date. print("\n"); print("".return_avatar_image($avatar)."\n"); - print("
".$text.$text_editby."\n"); + print("
".$text.$text_editby."\n"); print("\n"); $actionbar = "\"Quote\"". "\"Add".(user_can('commanage') ? "\"Delete\"" : "").($row["user"] == $CURUSER["id"] || get_user_class() >= $commanage_class ? "\"Edit\""."" : ""); @@ -5319,7 +5321,7 @@ function torrentTags($tags = 0, $type = 'checkbox') return $html; } -function saveSetting($prefix, $nameAndValue, $autoload = 'yes') +function saveSetting(string $prefix, array $nameAndValue, string $autoload = 'yes'): void { $prefix = strtolower($prefix); $datetimeNow = date('Y-m-d H:i:s'); @@ -5907,7 +5909,11 @@ function get_ip_location_from_geoip($ip): bool|array function msgalert($url, $text, $bgcolor = "red") { print("
\n"); - print("".$text.""); + if (!empty($url)) { + print("".$text.""); + } else { + print("".$text.""); + } print("

"); } @@ -6078,7 +6084,7 @@ function calculate_seed_bonus($uid, $torrentIdArr = null): array do_log(sprintf( "$logPrefix, torrent: %s, peer ID: %s, weeks: %s, size_raw: %s GB, size: %s GB, increase A: %s, increase official A: %s", $torrent['id'], $torrent['peerID'], $weeks_alive, $gb_size_raw, $gb_size, $temp, $officialAIncrease - )); + ), "debug"); } if ($count > $maxseeding_bonus) $count = $maxseeding_bonus; @@ -6315,7 +6321,7 @@ function build_bonus_table(array $user, array $bonusResult = [], array $options $isDonor = is_donor($user); $donortimes_bonus = get_setting('bonus.donortimes'); $baseBonusFactor = 1; - if ($isDonor) { + if ($isDonor && $donortimes_bonus != 0) { $baseBonusFactor = $donortimes_bonus; } $baseBonus = $bonusResult['seed_bonus'] * $baseBonusFactor; diff --git a/include/globalfunctions.php b/include/globalfunctions.php index 2206ffa4..e3e35466 100644 --- a/include/globalfunctions.php +++ b/include/globalfunctions.php @@ -788,6 +788,15 @@ function get_user_id() return auth()->user()->id ?? 0; } +function get_user_passkey() +{ + if (IN_NEXUS) { + global $CURUSER; + return $CURUSER["passkey"] ?? ""; + } + return auth()->user()->passkey ?? ""; +} + function get_pure_username() { if (IN_NEXUS) { @@ -928,13 +937,13 @@ function getDataTraffic(array $torrent, array $queries, array $user, $peer, $sna } $uploaderRatio = get_setting('torrent.uploaderdouble'); $log .= ", uploaderRatio: $uploaderRatio"; - if ($torrent['owner'] == $user['id']) { + if ($torrent['owner'] == $user['id'] && $uploaderRatio != 1) { //uploader, use the bigger one $upRatio = max($uploaderRatio, \App\Models\Torrent::$promotionTypes[$spStateReal]['up_multiplier']); - $log .= ", [IS_UPLOADER], upRatio: $upRatio"; + $log .= ", [IS_UPLOADER] && uploaderRatio != 1, upRatio: $upRatio"; } else { $upRatio = \App\Models\Torrent::$promotionTypes[$spStateReal]['up_multiplier']; - $log .= ", [IS_NOT_UPLOADER], upRatio: $upRatio"; + $log .= ", [IS_NOT_UPLOADER] || uploaderRatio == 1, upRatio: $upRatio"; } /** * VIP do not calculate downloaded @@ -1238,6 +1247,9 @@ function get_snatch_info($torrentId, $userId) return mysql_fetch_assoc(sql_query(sprintf('select * from snatched where torrentid = %s and userid = %s order by id desc limit 1', $torrentId, $userId))); } +/** + * 完整的 Laravel 事件, 在 php 端有监听者的需要触发. 同样会执行 publish_model_event() + */ function fire_event(string $name, \Illuminate\Database\Eloquent\Model $model, \Illuminate\Database\Eloquent\Model $oldModel = null): void { $prefix = "fire_event:"; @@ -1250,3 +1262,21 @@ function fire_event(string $name, \Illuminate\Database\Eloquent\Model $model, \I } executeCommand("event:fire --name=$name --idKey=$idKey --idKeyOld=$idKeyOld", "string", true, false); } + +/** + * 仅仅是往 redis 发布事件, php 端无监听者仅在其他平台有需要的触发这个即可, 较轻量 + */ +function publish_model_event(string $event, int $id): void +{ + $channel = nexus_env("CHANNEL_NAME_MODEL_EVENT"); + if (!empty($channel)) { + \Nexus\Database\NexusDB::redis()->publish($channel, json_encode(["event" => $event, "id" => $id])); + } else { + do_log("event: $event, id: $id, channel: $channel, channel is empty!", "error"); + } +} + +function convertNamespaceToSnake(string $str): string +{ + return str_replace(["\\", "::"], ["_", "."], $str); +} diff --git a/lang/cht/lang_details.php b/lang/cht/lang_details.php index 5dfcca75..18616b2c 100644 --- a/lang/cht/lang_details.php +++ b/lang/cht/lang_details.php @@ -208,6 +208,7 @@ $lang_details = array 'text_creator' => "主創: ", 'submit_search_at_shooter' => "搜索射手網", 'submit_search_at_opensubtitles' => "搜索Opensubtitles", + 'title_show_or_hide' => "顯示 或 隱藏", 'title_bookmark' => "收藏", 'text_album_information' => "專輯資訊:", 'text_about_album' => "關于該專輯:", diff --git a/nexus/Database/DBMysqli.php b/nexus/Database/DBMysqli.php index 1f04d053..c1d6dc56 100644 --- a/nexus/Database/DBMysqli.php +++ b/nexus/Database/DBMysqli.php @@ -12,8 +12,8 @@ class DBMysqli implements DBInterface if (mysqli_connect_errno()) { throw new DatabaseException(mysqli_connect_error()); } - $mysqli->query("SET NAMES UTF8"); - $mysqli->query("SET collation_connection = 'utf8_general_ci'"); + $mysqli->set_charset("utf8mb4"); + $mysqli->query("SET collation_connection = 'utf8mb4_unicode_ci'"); $mysqli->query("SET sql_mode=''"); $mysqli->query("SET time_zone='".date('P')."'"); diff --git a/nexus/Install/Update.php b/nexus/Install/Update.php index e594af87..a84fc971 100644 --- a/nexus/Install/Update.php +++ b/nexus/Install/Update.php @@ -332,15 +332,6 @@ class Update extends Install ["value" => User::query()->where("class", User::CLASS_STAFF_LEADER)->first(["id"])->id] ); } - - /** - * @since 1.8.13 - */ - $settingName = "__has_fix_exam_index_UPLOAD_TORRENT_COUNT"; - $hasFixExamIndexUploadTorrentCount = get_setting($settingName, false); - if (!$hasFixExamIndexUploadTorrentCount) { - - } } public function runExtraMigrate() diff --git a/nexus/Plugin/BasePlugin.php b/nexus/Plugin/BasePlugin.php index c3643fb9..3219bb4f 100644 --- a/nexus/Plugin/BasePlugin.php +++ b/nexus/Plugin/BasePlugin.php @@ -2,9 +2,7 @@ namespace Nexus\Plugin; use App\Repositories\BaseRepository; -use Illuminate\Support\Arr; use Illuminate\Support\Facades\Artisan; -use Nexus\Database\NexusDB; abstract class BasePlugin extends BaseRepository { @@ -44,4 +42,19 @@ abstract class BasePlugin extends BaseRepository $pluginRoot = dirname($reflection->getFileName(), 2); return $pluginRoot . "/resources/views/" . trim($name, "/"); } + + public function trans($name): string + { + return nexus_trans($this->getTransKey($name)); + } + + public function getTransKey($name): string + { + return sprintf("%s::%s", static::ID, $name); + } + + public static function getInstance(): static + { + return Plugin::getById(static::ID); + } } diff --git a/nexus/Plugin/Plugin.php b/nexus/Plugin/Plugin.php index 93633dbe..7ec962dc 100644 --- a/nexus/Plugin/Plugin.php +++ b/nexus/Plugin/Plugin.php @@ -23,6 +23,16 @@ class Plugin return !empty(self::$providers[$name]['providers']); } + public static function listEnabled(): array + { + $result = []; + //plugins are more exactly + foreach (self::$plugins as $id => $plugin) { + $result[$id] = 1; + } + return $result; + } + public static function getById($id) :BasePlugin|null { return self::$plugins[$id] ?? null; diff --git a/public/announce.php b/public/announce.php index 50370fe5..04b9cb81 100644 --- a/public/announce.php +++ b/public/announce.php @@ -164,6 +164,13 @@ if (!$az) { $redis->set("$passkeyInvalidKey:$passkey", TIMENOW, ['ex' => 24*3600]); warn("Invalid passkey! Re-download the .torrent from $BASEURL"); } +if ($az["enabled"] == "no") + warn("Your account is disabled!", 300); +elseif ($az["parked"] == "yes") + warn("Your account is parked! (Read the FAQ)", 300); +elseif ($az["downloadpos"] == "no") + warn("Your downloading privileges have been disabled! (Read the rules)", 300); + $userid = intval($az['id'] ?? 0); unset($GLOBALS['CURUSER']); $CURUSER = $GLOBALS["CURUSER"] = $az; @@ -401,13 +408,6 @@ if (!isset($self)) if ($valid[0] >= 1 && $seeder == 'no') err("You already are downloading the same torrent. You may only leech from one location at a time.", 300); if ($valid[0] >= 3 && $seeder == 'yes') err("You cannot seed the same torrent from more than 3 locations.", 300); - if ($az["enabled"] == "no") - warn("Your account is disabled!", 300); - elseif ($az["parked"] == "yes") - warn("Your account is parked! (Read the FAQ)", 300); - elseif ($az["downloadpos"] == "no") - warn("Your downloading privileges have been disabled! (Read the rules)", 300); - if ($az["class"] < UC_VIP) { $ratio = (($az["downloaded"] > 0) ? ($az["uploaded"] / $az["downloaded"]) : 1); @@ -451,42 +451,30 @@ if (!isset($self)) && $torrent['owner'] != $userid && get_setting("torrent.paid_torrent_enabled") == "yes" ) { - $hasBuyCacheKey = \App\Repositories\TorrentRepository::BOUGHT_USER_CACHE_KEY_PREFIX . $torrentid; - $hasBuy = $redis->hGet($hasBuyCacheKey, $userid); - if ($hasBuy === false) { - //no cache - $lockName = "load_torrent_bought_user:$torrentid"; - $loadBoughtLock = new \Nexus\Database\NexusLock($lockName, 300); - if ($loadBoughtLock->get()) { - //get lock, do load - executeCommand("torrent:load_bought_user $torrentid", "string", true, false); - } else { - do_log("can not get loadBoughtLock: $lockName", 'debug'); + $torrentRep = new \App\Repositories\TorrentRepository(); + $buyStatus = $torrentRep->getBuyStatus($userid, $torrentid); + if ($buyStatus > 0) { + do_log(sprintf("user: %v buy torrent: %v fail count: %v", $userid, $torrentid, $buyStatus), "error"); + if ($buyStatus > 3) { + //warn + \App\Utils\MsgAlert::getInstance()->add( + "announce_paid_torrent_too_many_times", + time() + 86400, + "announce to paid torrent and fail too many times, please make sure you have enough bonus!", + "", + "black" + ); } - //simple cache the hasBuy result - $hasBuy = \Nexus\Database\NexusDB::remember(sprintf("user_has_buy_torrent:%s:%s", $userid, $torrentid), 86400*10, function () use($userid, $torrentid) { - $exists = \App\Models\TorrentBuyLog::query()->where('uid', $userid)->where('torrent_id', $torrentid)->exists(); - return intval($exists); - }); + if ($buyStatus > 10) { + //disable download + (new \App\Repositories\UserRepository())->updateDownloadPrivileges(null, $userid, 'no', 'announce_paid_torrent_too_many_times'); + } + warn("purchase fail, please try again later, please make sure you have enough bonus", 300); } - if (!$hasBuy) { - $lock = new \Nexus\Database\NexusLock("buying_torrent:$userid", 5); - if (!$lock->get()) { - $msg = "buying torrent, wait!"; - do_log("[ANNOUNCE] user: $userid, torrent: $torrentid, $msg", 'error'); - err($msg); - } - $bonusRep = new \App\Repositories\BonusRepository(); - try { - $bonusRep->consumeToBuyTorrent($az['id'], $torrent['id'], 'Web'); - $redis->hSet($hasBuyCacheKey, $userid, 1); - $lock->release(); - } catch (\Exception $exception) { - $msg = $exception->getMessage(); - do_log("[ANNOUNCE] user: $userid, torrent: $torrentid, $msg " . $exception->getTraceAsString(), 'error'); - $lock->release(); - err($msg); - } + if ($buyStatus == \App\Repositories\TorrentRepository::BUY_STATUS_UNKNOWN) { + //just enqueue job + \App\Utils\ThirdPartyJob::addBuyTorrent($userid, $torrentid); + warn("purchase in progress, please wait", 300); } } } @@ -535,7 +523,7 @@ if (isset($self) && $event == "stopped") sql_query("DELETE FROM peers WHERE id = {$self['id']}") or err("D Err"); if (mysql_affected_rows() && !empty($snatchInfo)) { -// $updateset[] = ($self["seeder"] == "yes" ? "seeders = seeders - 1" : "leechers = leechers - 1"); + $updateset[] = ($self["seeder"] == "yes" ? "seeders = seeders - 1" : "leechers = leechers - 1"); $hasChangeSeederLeecher = true; sql_query("UPDATE snatched SET uploaded = uploaded + $trueupthis, downloaded = downloaded + $truedownthis, to_go = $left, $announcetime, last_action = ".$dt." WHERE id = {$snatchInfo['id']}") or err("SL Err 1"); } @@ -556,7 +544,7 @@ elseif(isset($self)) if (mysql_affected_rows()) { if ($seeder <> $self["seeder"]) { -// $updateset[] = ($seeder == "yes" ? "seeders = seeders + 1, leechers = leechers - 1" : "seeders = seeders - 1, leechers = leechers + 1"); + $updateset[] = ($seeder == "yes" ? "seeders = seeders + 1, leechers = leechers - 1" : "seeders = seeders - 1, leechers = leechers + 1"); $hasChangeSeederLeecher = true; } if (!empty($snatchInfo)) { @@ -570,19 +558,7 @@ else if ($event != 'stopped') { $isPeerExistResultSet = sql_query("select id from peers where $selfwhere limit 1"); 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'; - }); + $connectable = "yes"; $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, is_seed_box) 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).", ".intval($isIPSeedBox).")"; do_log("[INSERT PEER] peer not exists for $selfwhere, do insert with $insertPeerSql"); @@ -590,7 +566,7 @@ else sql_query($insertPeerSql) or err("PL Err 2"); if (mysql_affected_rows()) { -// $updateset[] = ($seeder == "yes" ? "seeders = seeders + 1" : "leechers = leechers + 1"); + $updateset[] = ($seeder == "yes" ? "seeders = seeders + 1" : "leechers = leechers + 1"); $hasChangeSeederLeecher = true; // $check = @mysql_fetch_row(@sql_query("SELECT COUNT(*) FROM snatched WHERE torrentid = $torrentid AND userid = $userid")); $checkSnatchedRes = mysql_fetch_assoc(sql_query("SELECT id FROM snatched WHERE torrentid = $torrentid AND userid = $userid limit 1")); @@ -617,8 +593,8 @@ if (($left > 0 || $event == "completed") && $az['class'] < \App\Models\HitAndRun $hrLog = sprintf("[HR_LOG] user: %d, torrent: %d, hrMode: %s", $userid, $torrentid, $hrMode); if ($hrMode == \App\Models\HitAndRun::MODE_GLOBAL || ($hrMode == \App\Models\HitAndRun::MODE_MANUAL && $torrent['hr'] == \App\Models\Torrent::HR_YES)) { $hrCacheKey = sprintf("hit_and_run:%d:%d", $userid, $torrentid); - $hrExists = \Nexus\Database\NexusDB::remember($hrCacheKey, 24*3600, function () use ($torrentid, $userid) { - return \App\Models\HitAndRun::query()->where("uid", $userid)->where("torrent_id", $torrentid)->exists(); + $hrExists = \Nexus\Database\NexusDB::remember($hrCacheKey, mt_rand(86400*365*5, 86400*365*10), function () use ($torrentid, $userid) { + return \App\Models\HitAndRun::query()->where("uid", $userid)->where("torrent_id", $torrentid)->exists() ? 1 : 0; }); $hrLog .= ", hrExists: $hrExists"; if (!$hrExists) { @@ -650,11 +626,11 @@ if (($left > 0 || $event == "completed") && $az['class'] < \App\Models\HitAndRun do_log("$hrLog, not match", "debug"); } } - -if (isset($event) && !empty($event)) { - $updateset[] = 'seeders = ' . get_row_count("peers", "where torrent = $torrentid and to_go = 0"); - $updateset[] = 'leechers = ' . get_row_count("peers", "where torrent = $torrentid and to_go > 0"); -} +// revert to only increment/decrement +//if (isset($event) && !empty($event)) { +// $updateset[] = 'seeders = ' . get_row_count("peers", "where torrent = $torrentid and to_go = 0"); +// $updateset[] = 'leechers = ' . get_row_count("peers", "where torrent = $torrentid and to_go > 0"); +//} if (count($updateset) || $hasChangeSeederLeecher) // Update only when there is change in peer counts { diff --git a/public/attachment.php b/public/attachment.php index c70d3d04..a2290dfb 100644 --- a/public/attachment.php +++ b/public/attachment.php @@ -109,7 +109,7 @@ if ($Attach->enable_attachment()) if ($orig && !$stop) { $thumb = imagecreatetruecolor($newwidth, $newheight); - imagecopyresized($thumb, $orig, 0, 0, 0, 0, $newwidth, $newheight, $width, $height); + imagecopyresampled($thumb, $orig, 0, 0, 0, 0, $newwidth, $newheight, $width, $height); if ($thumbnailtype_attachment == 'createthumb'){ $hasthumb = true; imagejpeg($thumb, $file_location.".".$ext.".thumb.jpg", $thumbquality_attachment); diff --git a/public/bitbucket-upload.php b/public/bitbucket-upload.php index bcbfb7b7..da598450 100644 --- a/public/bitbucket-upload.php +++ b/public/bitbucket-upload.php @@ -48,7 +48,7 @@ if ($_SERVER["REQUEST_METHOD"] == "POST") if(!$orig) stderr($lang_bitbucketupload['std_image_processing_failed'],$lang_bitbucketupload['std_sorry_the_uploaded']."$imgtypes[$it]".$lang_bitbucketupload['std_failed_processing']); $thumb = imagecreatetruecolor($newwidth, $newheight); - imagecopyresized($thumb, $orig, 0, 0, 0, 0, $newwidth, $newheight, $width, $height); + imagecopyresampled($thumb, $orig, 0, 0, 0, 0, $newwidth, $newheight, $width, $height); switch ($it) { case 1: $ret = imagegif($thumb, $tgtfile); diff --git a/public/contactstaff.php b/public/contactstaff.php index a00f89d6..17d9d832 100644 --- a/public/contactstaff.php +++ b/public/contactstaff.php @@ -7,8 +7,6 @@ loggedinorreturn(); stdhead($lang_contactstaff['head_contact_staff'], false); begin_main_frame(); print("
"); -if (isset($_GET["returnto"]) && $_GET["returnto"] || $_SERVER["HTTP_REFERER"]) - print(""); begin_compose($lang_contactstaff['text_message_to_staff'], "new"); end_compose(); print("
"); diff --git a/public/details.php b/public/details.php index 92113599..24dc8eeb 100644 --- a/public/details.php +++ b/public/details.php @@ -6,7 +6,7 @@ require_once(get_langfile_path()); loggedinorreturn(); $id = intval($_GET["id"] ?? 0); $customField = new \Nexus\Field\Field(); -int_check($id); +int_check($id, true); if (!isset($id) || !$id) die(); @@ -483,7 +483,7 @@ WHERE " . $where_area . " ORDER BY torrents.id DESC") or sqlerr(__FILE__, __LINE tr($lang_details['row_hot_meter'], "
" . $lang_details['text_views']."". $row["views"] . "" . $lang_details['text_hits']. "" . $row["hits"] . "" .$lang_details['text_snatched'] . "" . $row["times_completed"]. $lang_details['text_view_snatches'] . "" . $lang_details['row_last_seeder']. "" . gettime($row["last_action"]) . "
",1); $bwres = sql_query("SELECT uploadspeed.name AS upname, downloadspeed.name AS downname, isp.name AS ispname FROM users LEFT JOIN uploadspeed ON users.upload = uploadspeed.id LEFT JOIN downloadspeed ON users.download = downloadspeed.id LEFT JOIN isp ON users.isp = isp.id WHERE users.id=".$row['owner']); $bwrow = mysql_fetch_array($bwres); - if ($bwrow['upname'] && $bwrow['downname']) + if (isset($bwrow['upname']) && isset($bwrow['downname']) && $bwrow['upname'] && $bwrow['downname']) tr($lang_details['row_uploader_bandwidth'], "\"Downstream ".$bwrow['downname']."    \"Upstream ".$bwrow['upname']."    ".$bwrow['ispname'],1); /* diff --git a/public/forums.php b/public/forums.php index 12ce4ce0..acddccbe 100644 --- a/public/forums.php +++ b/public/forums.php @@ -765,7 +765,7 @@ if ($action == "viewtopic") print("\n"); - $body = "
"; + $body = "
"; //hidden content applied to second or higher floor post (for whose user class below Ad , not poster , not mods ,not reply's author) // if ($protected_enabled && $pn+$offset>1 && get_user_class()1 && !can_view_post($userid, $arr)){ diff --git a/public/getusertorrentlistajax.php b/public/getusertorrentlistajax.php index 279d6151..e3d08c89 100644 --- a/public/getusertorrentlistajax.php +++ b/public/getusertorrentlistajax.php @@ -259,7 +259,7 @@ function maketable($res, $mode = 'seeding') return [$ret, $total_size]; } $count = 0; -$torrentlist = ""; +$torrentlist = $pagertop = $pagerbottom = ""; switch ($type) { case 'uploaded': diff --git a/public/messages.php b/public/messages.php index 56db507c..886c67ad 100644 --- a/public/messages.php +++ b/public/messages.php @@ -228,7 +228,7 @@ $sender = get_username($message['sender']); $reply = " [ ".$lang_messages['text_reply']." ]"; } } -$body = format_comment($message['msg'], false); +$body = format_comment($message['msg'], true); //$body = htmlspecialchars_decode($body); $added = $message['added']; if ($message['sender'] == $CURUSER['id']) diff --git a/public/mybonus.php b/public/mybonus.php index 0ad399c8..08339e78 100644 --- a/public/mybonus.php +++ b/public/mybonus.php @@ -301,7 +301,7 @@ if ($bonus_tweak == "disable" || $bonus_tweak == "disablesave") stderr($lang_mybonus['std_sorry'],$lang_mybonus['std_karma_system_disabled'].($bonus_tweak == "disablesave" ? "".$lang_mybonus['std_points_active']."" : ""),false); $action = htmlspecialchars($_GET['action'] ?? ''); -$do = htmlspecialchars($_GET['do'] ?? null); +$do = htmlspecialchars($_GET['do'] ?? ''); unset($msg); if (isset($do)) { if ($do == "upload") @@ -398,7 +398,7 @@ for ($i=0; $i < count($allBonus); $i++) elseif ($bonusarray['art'] == 'noad'){ if ($enablenoad_advertisement == 'yes' && get_user_class() >= $noad_advertisement) print("
"); - elseif (strtotime($CURUSER['noaduntil']) >= TIMENOW) + elseif (!empty($CURUSER['noaduntil']) && strtotime($CURUSER['noaduntil']) >= TIMENOW) print(""); elseif (get_user_class() < $bonusnoad_advertisement) print(""); @@ -550,8 +550,6 @@ if ($offervote_bonus > 0) print("
  • ".$lang_mybonus['text_offer_vote'].$offervote_bonus.$lang_mybonus['text_point'].add_s($offervote_bonus)."
  • "); if ($funboxvote_bonus > 0) print("
  • ".$lang_mybonus['text_funbox_vote'].$funboxvote_bonus.$lang_mybonus['text_point'].add_s($funboxvote_bonus)."
  • "); -if ($ratetorrent_bonus > 0) - print("
  • ".$lang_mybonus['text_rate_torrent'].$ratetorrent_bonus.$lang_mybonus['text_point'].add_s($ratetorrent_bonus)."
  • "); if ($saythanks_bonus > 0) print("
  • ".$lang_mybonus['text_say_thanks'].$saythanks_bonus.$lang_mybonus['text_point'].add_s($saythanks_bonus)."
  • "); if ($receivethanks_bonus > 0) @@ -611,6 +609,10 @@ if ($action == "exchange") { else { $upload = $CURUSER['uploaded']; $up = $upload + $bonusarray['menge']; + do_log(sprintf( + "user: %s going to use %s bonus to exchange uploaded from %s to %s", + $CURUSER['id'], $points, $CURUSER['uploaded'], $up + )); // $bonuscomment = date("Y-m-d") . " - " .$points. " Points for upload bonus.\n " .$bonuscomment; // sql_query("UPDATE users SET uploaded = ".sqlesc($up).", seedbonus = seedbonus - $points, bonuscomment = ".sqlesc($bonuscomment)." WHERE id = ".sqlesc($userid)) or sqlerr(__FILE__, __LINE__); $bonusRep->consumeUserBonus($CURUSER['id'], $points, \App\Models\BonusLogs::BUSINESS_TYPE_EXCHANGE_UPLOAD, $points. " Points for uploaded.", ['uploaded' => $up]); @@ -620,6 +622,10 @@ if ($action == "exchange") { if($art == "traffic_downloaded") { $downloaded = $CURUSER['downloaded']; $down = $downloaded + $bonusarray['menge']; + do_log(sprintf( + "user: %s going to use %s bonus to exchange downloaded from %s to %s", + $CURUSER['id'], $points, $CURUSER['downloaded'], $down + )); $bonusRep->consumeUserBonus($CURUSER['id'], $points, \App\Models\BonusLogs::BUSINESS_TYPE_EXCHANGE_DOWNLOAD, $points. " Points for downloaded.", ['downloaded' => $down]); nexus_redirect("" . get_protocol_prefix() . "$BASEURL/mybonus.php?do=download"); } diff --git a/public/shoutbox.php b/public/shoutbox.php index 0701547f..72262de0 100644 --- a/public/shoutbox.php +++ b/public/shoutbox.php @@ -127,7 +127,7 @@ else } else $username = $lang_shoutbox['text_guest']; if (isset($CURUSER) && $CURUSER['timetype'] != 'timealive') - $time = strftime("%m.%d %H:%M",$arr["date"]); + $time = (new DateTime())->setTimestamp($arr["date"])->format('m.d H:i'); else $time = get_elapsed_time($arr["date"]).$lang_shoutbox['text_ago']; print("".(user_can('userprofile') || $arr['userid'] == $CURUSER['id'] ? "" : "")."\n"); + print("".(user_can('userprofile') ? "" : "")."\n"); } print("
    [".$time."] ". $del ." ". $username." " . format_comment($arr["text"],true,false,true,true,600,false,false)." diff --git a/public/styles/nexus.css b/public/styles/nexus.css index c63fcfa0..17ac9f35 100644 --- a/public/styles/nexus.css +++ b/public/styles/nexus.css @@ -71,3 +71,6 @@ img.hitandrun { .codemain>pre { margin: 0; } +.word-break-all { + word-break: break-all; +} diff --git a/public/takesignup.php b/public/takesignup.php index bfe2ec15..4c4f43c9 100644 --- a/public/takesignup.php +++ b/public/takesignup.php @@ -151,6 +151,7 @@ $secret = mksecret(); $wantpasshash = md5($secret . $wantpassword . $secret); $editsecret = ($verification == 'admin' ? '' : $secret); $invite_count = (int) $invite_count; +$passkey = md5($wantusername.date("Y-m-d H:i:s").$wantpasshash); $wantusername = sqlesc($wantusername); $wantpasshash = sqlesc($wantpasshash); @@ -167,8 +168,9 @@ $res_check_user = sql_query("SELECT * FROM users WHERE username = " . $wantusern if(mysql_num_rows($res_check_user) == 1) bark($lang_takesignup['std_username_exists']); -$ret = sql_query("INSERT INTO users (username, passhash, secret, editsecret, email, country, gender, status, class, invites, ".($type == 'invite' ? "invited_by," : "")." added, last_access, lang, stylesheet".($showschool == 'yes' ? ", school" : "").", uploaded) VALUES (" . $wantusername . "," . $wantpasshash . "," . $secret . "," . $editsecret . "," . $email . "," . $country . "," . $gender . ", 'pending', ".$defaultclass_class.",". $invite_count .", ".($type == 'invite' ? "'$inviter'," : "") ." '". date("Y-m-d H:i:s") ."' , " . " '". date("Y-m-d H:i:s") ."' , ".$sitelangid . ",".$defcss.($showschool == 'yes' ? ",".$school : "").",".($iniupload_main > 0 ? $iniupload_main : 0).")") or sqlerr(__FILE__, __LINE__); +$ret = sql_query("INSERT INTO users (username, passhash, passkey, secret, editsecret, email, country, gender, status, class, invites, ".($type == 'invite' ? "invited_by," : "")." added, last_access, lang, stylesheet".($showschool == 'yes' ? ", school" : "").", uploaded) VALUES (" . $wantusername . "," . $wantpasshash . "," . sqlesc($passkey) . "," . $secret . "," . $editsecret . "," . $email . "," . $country . "," . $gender . ", 'pending', ".$defaultclass_class.",". $invite_count .", ".($type == 'invite' ? "'$inviter'," : "") ." '". date("Y-m-d H:i:s") ."' , " . " '". date("Y-m-d H:i:s") ."' , ".$sitelangid . ",".$defcss.($showschool == 'yes' ? ",".$school : "").",".($iniupload_main > 0 ? $iniupload_main : 0).")") or sqlerr(__FILE__, __LINE__); $id = mysql_insert_id(); +fire_event("user_created", \App\Models\User::query()->first($id, \App\Models\User::$commonFields)); $tmpInviteCount = get_setting('main.tmp_invite_count'); if ($tmpInviteCount > 0) { $userRep = new \App\Repositories\UserRepository(); diff --git a/public/torrentrss.php b/public/torrentrss.php index 68c6afb3..426efc4a 100644 --- a/public/torrentrss.php +++ b/public/torrentrss.php @@ -71,7 +71,7 @@ if (isset($searchstr)){ $ANDOR = ($search_mode == 0 ? " AND " : " OR "); // only affects mode 0 and mode 1 foreach ($like_expression_array as &$like_expression_array_element) - $like_expression_array_element = "(torrents.name" . $like_expression_array_element.($_GET['ismalldescr'] ? " OR torrents.small_descr". $like_expression_array_element : "").")"; + $like_expression_array_element = "(torrents.name" . $like_expression_array_element . (isset($_GET['ismalldescr']) && $_GET['ismalldescr'] ? " OR torrents.small_descr" . $like_expression_array_element : "") . ")"; $wherea[] = implode($ANDOR, $like_expression_array); $where .= ($where ? " AND " : "") . implode(" AND ", $wherea); } diff --git a/public/torrents.php b/public/torrents.php index ee11320c..4a621e88 100644 --- a/public/torrents.php +++ b/public/torrents.php @@ -702,7 +702,6 @@ $search_area = 0; if (isset($searchstr)) { if (!isset($_GET['notnewword']) || !$_GET['notnewword']){ - insert_suggest($searchstr, $CURUSER['id']); $notnewword=""; } else{ @@ -956,6 +955,9 @@ do_log("[TORRENT_COUNT_SQL] $sql", 'debug'); if ($count) { + if (isset($searchstr) && (!isset($_GET['notnewword']) || !$_GET['notnewword'])){ + insert_suggest($searchstr, $CURUSER['id']); + } if ($addparam != "") { if ($pagerlink != "") diff --git a/public/userdetails.php b/public/userdetails.php index 5cd20215..830e2a44 100644 --- a/public/userdetails.php +++ b/public/userdetails.php @@ -238,7 +238,7 @@ if (user_can('userprofile') || $user["id"] == $CURUSER["id"]) 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(); +$res = sql_query("SELECT peer_id, agent, ipv4, ipv6, port FROM peers WHERE userid = {$user['id']} GROUP BY agent, ipv4, ipv6, port") or sqlerr(); if (mysql_num_rows($res) > 0) { $clientselect .= ""; diff --git a/public/viewpeerlist.php b/public/viewpeerlist.php index 813902d2..446e9d67 100644 --- a/public/viewpeerlist.php +++ b/public/viewpeerlist.php @@ -168,16 +168,24 @@ function dltable($name, $arr, $torrent, &$isSeedBoxCaseWhens) $s .= "
    AgentIPV4IPV6Port
    \n"; return $s; } - $downloaders = array(); - $seeders = array(); - $torrent = \App\Models\Torrent::query()->findOrFail($id, ['id', 'seeders', 'leechers']); - $subres = sql_query("SELECT id, seeder, finishedat, downloadoffset, uploadoffset, ip, ipv4, ipv6, port, uploaded, downloaded, to_go, UNIX_TIMESTAMP(started) AS st, connectable, agent, peer_id, UNIX_TIMESTAMP(last_action) AS la, userid FROM peers WHERE torrent = $id") or sqlerr(); - while ($subrow = mysql_fetch_array($subres)) { - if ($subrow["seeder"] == "yes") - $seeders[] = $subrow; - else - $downloaders[] = $subrow; - } + $downloaders = array(); + $seeders = array(); + $torrent = \App\Models\Torrent::query()->findOrFail($id, ['id', 'seeders', 'leechers']); + $seedersAndLeechers = apply_filter("torrent_seeder_leecher_list", [], $id); + if (isset($seedersAndLeechers['seeders'], $seedersAndLeechers['leechers'])) { +// dd($seedersAndLeechers); + $seeders = $seedersAndLeechers['seeders']; + $downloaders = $seedersAndLeechers['leechers']; + do_log("SEEDER_LEECHER_FROM_FILTER: torrent_seeder_leecher_list"); + } else { + $subres = sql_query("SELECT id, seeder, finishedat, downloadoffset, uploadoffset, ip, ipv4, ipv6, port, uploaded, downloaded, to_go, UNIX_TIMESTAMP(started) AS st, connectable, agent, peer_id, UNIX_TIMESTAMP(last_action) AS la, userid FROM peers WHERE torrent = $id") or sqlerr(); + while ($subrow = mysql_fetch_array($subres)) { + if ($subrow["seeder"] == "yes") + $seeders[] = $subrow; + else + $downloaders[] = $subrow; + } + } $seedersCount = count($seeders); $leechersCount = count($downloaders); if ($torrent->seeders != $seedersCount || $torrent->leechers != $leechersCount) { diff --git a/public/viewsnatches.php b/public/viewsnatches.php index 1dd22577..49888e29 100644 --- a/public/viewsnatches.php +++ b/public/viewsnatches.php @@ -53,7 +53,7 @@ if ($count){ } else $username = get_username($arr['userid']); $reportImage = "\"Report\""; - print("
    " . $username ."".$arr['ip'].$seedBoxRep->renderIcon($arr['ip'], $arr['userid'])."".$uploaded."@".$uprate.$lang_viewsnatches['text_per_second']."
    ".$downloaded."@".$downrate.$lang_viewsnatches['text_per_second']."
    $ratio$seedtime$leechtime".gettime($arr['completedat'],true,false)."".gettime($arr['last_action'],true,false)."".($userrow['privacy'] != 'strong' || user_can('viewanonymous') ? "$reportImage" : $reportImage)."
    " . $username ."".$arr['ip'].$seedBoxRep->renderIcon($arr['ip'], $arr['userid'])."".$uploaded."@".$uprate.$lang_viewsnatches['text_per_second']."
    ".$downloaded."@".$downrate.$lang_viewsnatches['text_per_second']."
    $ratio$seedtime$leechtime".gettime($arr['completedat'],true,false)."".gettime($arr['last_action'],true,false)."".($userrow['privacy'] != 'strong' || user_can('viewanonymous') ? "$reportImage" : $reportImage)."
    \n"); print($pagerbottom); diff --git a/resources/lang/en/dashboard.php b/resources/lang/en/dashboard.php index 6c373525..d4306c20 100644 --- a/resources/lang/en/dashboard.php +++ b/resources/lang/en/dashboard.php @@ -47,6 +47,7 @@ return [ 'server_software' => 'Web software', 'load_average' => 'Server load average', 'filament_version' => 'Filament version', + 'redis_version' => 'Redis version', ], 'latest_user' => [ 'page_title' => 'User latest', diff --git a/resources/lang/en/message.php b/resources/lang/en/message.php index c9f9abf6..3b726590 100644 --- a/resources/lang/en/message.php +++ b/resources/lang/en/message.php @@ -19,6 +19,10 @@ return [ 'subject' => 'Download permission canceled', 'body' => 'Your download permission has been cancelled due to excessive upload speed, please file if you are a seed box user.' , ], + 'download_disable_announce_paid_torrent_too_many_times' => [ + 'subject' => 'Download permission canceled', + 'body' => 'Your download permission has been cancelled due to announce to paid torrent too many times, please make sure you have enough bonus.' , + ], 'download_enable' => [ 'subject' => 'Download permission restored', 'body' => 'Your download privileges restored, you can now download torrents. By: :operator', diff --git a/resources/lang/zh_CN/dashboard.php b/resources/lang/zh_CN/dashboard.php index 1a1f34f4..3772731e 100644 --- a/resources/lang/zh_CN/dashboard.php +++ b/resources/lang/zh_CN/dashboard.php @@ -47,6 +47,7 @@ return [ 'server_software' => 'Web 软件', 'load_average' => '服务器平均负载', 'filament_version' => 'Filament 版本', + 'redis_version' => 'Redis 版本', ], 'latest_user' => [ 'page_title' => '最新用户', diff --git a/resources/lang/zh_CN/message.php b/resources/lang/zh_CN/message.php index 4e195039..3c57994c 100644 --- a/resources/lang/zh_CN/message.php +++ b/resources/lang/zh_CN/message.php @@ -19,6 +19,10 @@ return [ 'subject' => '下载权限取消', 'body' => '你因上传速度过快下载权限被取消,若是盒子用户请备案。', ], + 'download_disable_announce_paid_torrent_too_many_times' => [ + 'subject' => '下载权限取消', + 'body' => '你因向付费种子汇报失败次数过多下载权限被取消,请确保你有足够的魔力。', + ], 'download_enable' => [ 'subject' => '下载权限恢复', 'body' => '你的下载权限恢复,你现在可以下载种子。By: :operator', diff --git a/resources/lang/zh_TW/dashboard.php b/resources/lang/zh_TW/dashboard.php index 63fcbdea..fb128dcf 100644 --- a/resources/lang/zh_TW/dashboard.php +++ b/resources/lang/zh_TW/dashboard.php @@ -47,6 +47,7 @@ return [ 'server_software' => 'Web 軟件', 'load_average' => '服務器平均負載', 'filament_version' => 'Filament 版本', + 'redis_version' => 'Redis 版本', ], 'latest_user' => [ 'page_title' => '最新用戶', diff --git a/resources/lang/zh_TW/message.php b/resources/lang/zh_TW/message.php index b1035fc9..0769ec95 100644 --- a/resources/lang/zh_TW/message.php +++ b/resources/lang/zh_TW/message.php @@ -18,6 +18,10 @@ return [ 'subject' => '下載權限取消', 'body' => '你因上傳速度過快下載權限被取消,若是盒子用戶請備案。', ], + 'download_disable_announce_paid_torrent_too_many_times' => [ + 'subject' => '下载权限取消', + 'body' => '你因向付費種子匯報失敗次數過多下載權限被取消,請確保你有足夠的魔力。', + ], 'download_enable' => [ 'subject' => '下載權限恢復', 'body' => '你的下載權限恢復,你現在可以下載種子。By: :operator', diff --git a/resources/views/filament/widgets/stat-table.blade.php b/resources/views/filament/widgets/stat-table.blade.php index 4cb9e4f5..b3a8ed2e 100644 --- a/resources/views/filament/widgets/stat-table.blade.php +++ b/resources/views/filament/widgets/stat-table.blade.php @@ -20,7 +20,7 @@ colspan="3" @endif > -
    {{$item['value']}}
    +
    {{$item['value']}}
    @endforeach diff --git a/server.php b/server.php index 5fb6379e..6ae40f5f 100644 --- a/server.php +++ b/server.php @@ -18,4 +18,4 @@ if ($uri !== '/' && file_exists(__DIR__.'/public'.$uri)) { return false; } -require_once __DIR__.'/public/index.php'; +require_once __DIR__.'/public/nexus.php';