Refactor IP History

This commit is contained in:
xiaomlove
2025-10-14 14:54:44 +07:00
parent 0f172a94be
commit ba8715a3f9
34 changed files with 494 additions and 131 deletions

View File

@@ -42,7 +42,7 @@ Welcome to participate in internationalization work, click [here](https://github
## System Requirements
- PHP: 8.2|8.3|8.4, must have extensions: bcmath, ctype, curl, fileinfo, json, mbstring, openssl, pdo_mysql, tokenizer, xml, mysqli, gd, redis, pcntl, sockets, posix, gmp, zend opcache, zip, intl, pdo_sqlite, sqlite3
- Mysql: 5.7 latest version or above
- Redis2.6.12 or above
- Redis4.0.0 or above
- Others: supervisor, rsync
## Quick Start

View File

@@ -42,7 +42,7 @@
## 系统要求
- PHP: 8.2|8.3|8.4必须扩展bcmath, ctype, curl, fileinfo, json, mbstring, openssl, pdo_mysql, tokenizer, xml, mysqli, gd, redis, pcntl, sockets, posix, gmp, zend opcache, zip, intl, pdo_sqlite, sqlite3
- Mysql: 5.7 最新版或以上版本
- Redis2.6.12 或以上版本
- Redis4.0.0 或以上版本
- 其他supervisor, rsync
## 快速开始

View File

@@ -6,6 +6,7 @@ use App\Jobs\CheckCleanup;
use App\Jobs\CheckQueueFailedJobs;
use App\Jobs\MaintainPluginState;
use App\Jobs\ManagePlugin;
use App\Jobs\SaveIpLogCacheToDB;
use App\Jobs\UpdateIsSeedBoxFromUserRecordsCache;
use App\Utils\ThirdPartyJob;
use Carbon\Carbon;
@@ -48,10 +49,10 @@ class Kernel extends ConsoleKernel
$schedule->command('meilisearch:import')->weeklyOn(1, "03:00");
$schedule->command('torrent:load_pieces_hash')->dailyAt("01:00");
$schedule->job(new CheckQueueFailedJobs())->everySixHours();
// $schedule->job(new ThirdPartyJob())->everyMinute();
$schedule->job(new MaintainPluginState())->everyMinute();
$schedule->job(new UpdateIsSeedBoxFromUserRecordsCache())->everySixHours();
$schedule->job(new CheckCleanup())->everyFifteenMinutes();
$schedule->job(new SaveIpLogCacheToDB())->hourly();
}

View File

@@ -0,0 +1,139 @@
<?php
namespace App\Filament\Resources\System\IpLogs;
use App\Filament\Resources\System\IpLogs\Pages\ManageIpLogs;
use App\Models\IpLog;
use BackedEnum;
use Filament\Actions\BulkActionGroup;
use Filament\Actions\DeleteAction;
use Filament\Actions\DeleteBulkAction;
use Filament\Actions\EditAction;
use Filament\Actions\ViewAction;
use Filament\Forms\Components\DateTimePicker;
use Filament\Forms\Components\TextInput;
use Filament\Resources\Resource;
use Filament\Schemas\Schema;
use Filament\Support\Icons\Heroicon;
use Filament\Tables\Columns\TextColumn;
use Filament\Tables\Filters\Filter;
use Filament\Tables\Table;
use Illuminate\Database\Eloquent\Builder;
use UnitEnum;
class IpLogResource extends Resource
{
protected static ?string $model = IpLog::class;
protected static string|BackedEnum|null $navigationIcon = Heroicon::OutlinedRectangleStack;
protected static ?int $navigationSort = 3;
protected static string | UnitEnum | null $navigationGroup = 'System';
public static function getLabel(): ?string
{
return __('ip-log.label');
}
public static function form(Schema $schema): Schema
{
return $schema
->components([
//
]);
}
public static function table(Table $table): Table
{
return $table
->columns([
TextColumn::make('userid')
->label('UID')
,
TextColumn::make('usernameForAdmin')
->label(__('label.username'))
,
TextColumn::make('ip')
->label('IP')
,
TextColumn::make('ipLocation')
->label(__('ip-log.ip_location'))
,
TextColumn::make('uri')
->label(__('ip-log.uri'))
,
TextColumn::make('count')
->label(__('ip-log.count'))
,
TextColumn::make('access')
->label(__('ip-log.access'))
->tooltip(__('ip-log.access_tooltip'))
,
])
->defaultSort('id', 'desc')
->filters([
Filter::make('uid')
->schema([
TextInput::make('uid')->label('UID'),
])
->query(function (Builder $query, array $data) {
return $query
->when(
$data['uid'],
fn (Builder $query, $value): Builder => $query->where('userid', $value),
);
}),
Filter::make('ip')
->schema([
TextInput::make('ip')->label('IP'),
])
->query(function (Builder $query, array $data) {
return $query
->when(
$data['ip'],
fn (Builder $query, $value): Builder => $query->where('ip', $value),
);
}),
Filter::make('access_begin')
->schema([
DateTimePicker::make('access_begin')->label(__('ip-log.access_begin')),
])
->query(function (Builder $query, array $data) {
return $query
->when(
$data['access_begin'],
fn (Builder $query, $value): Builder => $query->where('access', '>=', $value),
);
}),
Filter::make('access_end')
->schema([
DateTimePicker::make('access_end')->label(__('ip-log.access_end')),
])
->query(function (Builder $query, array $data) {
return $query
->when(
$data['access_end'],
fn (Builder $query, $value): Builder => $query->where('access', '<=', $value),
);
}),
])
->recordActions([
// ViewAction::make(),
// EditAction::make(),
// DeleteAction::make(),
])
->toolbarActions([
BulkActionGroup::make([
// DeleteBulkAction::make(),
]),
]);
}
public static function getPages(): array
{
return [
'index' => ManageIpLogs::route('/'),
];
}
}

View File

@@ -0,0 +1,20 @@
<?php
namespace App\Filament\Resources\System\IpLogs\Pages;
use App\Filament\PageListSingle;
use App\Filament\Resources\System\IpLogs\IpLogResource;
use Filament\Actions\CreateAction;
use Filament\Resources\Pages\ManageRecords;
class ManageIpLogs extends PageListSingle
{
protected static string $resource = IpLogResource::class;
protected function getHeaderActions(): array
{
return [
// CreateAction::make(),
];
}
}

View File

@@ -4,6 +4,7 @@ namespace App\Http;
use App\Http\Middleware\Filament;
use App\Http\Middleware\Locale;
use App\Http\Middleware\LogUserIp;
use Illuminate\Foundation\Http\Kernel as HttpKernel;
class Kernel extends HttpKernel
@@ -24,7 +25,7 @@ class Kernel extends HttpKernel
\App\Http\Middleware\TrimStrings::class,
// \Illuminate\Foundation\Http\Middleware\ConvertEmptyStringsToNull::class,
\App\Http\Middleware\BootNexus::class,
Locale::class,
LogUserIp::class,
];
/**
@@ -46,7 +47,6 @@ class Kernel extends HttpKernel
'api' => [
'throttle:api',
\Illuminate\Routing\Middleware\SubstituteBindings::class,
// \App\Http\Middleware\Platform::class,
],
'filament' => [
\Illuminate\Session\Middleware\StartSession::class,
@@ -73,8 +73,6 @@ class Kernel extends HttpKernel
'signed' => \Illuminate\Routing\Middleware\ValidateSignature::class,
'throttle' => \Illuminate\Routing\Middleware\ThrottleRequestsWithRedis::class,
'verified' => \Illuminate\Auth\Middleware\EnsureEmailIsVerified::class,
'permission' => \App\Http\Middleware\Permission::class,
'admin' => \App\Http\Middleware\Admin::class,
'locale' => \App\Http\Middleware\Locale::class,
'checkUserStatus' => \App\Http\Middleware\CheckUserStatus::class,
];

View File

@@ -1,30 +0,0 @@
<?php
namespace App\Http\Middleware;
use App\Models\User;
use Closure;
use Illuminate\Http\Request;
use Illuminate\Validation\UnauthorizedException;
class Admin
{
/**
* Handle an incoming request.
*
* @param \Illuminate\Http\Request $request
* @param \Closure $next
* @return mixed
*/
public function handle(Request $request, Closure $next)
{
/** @var CheckUserStatus $user */
$user = $request->user();
if (!$user || !$user->canAccessAdmin()) {
do_log("denied!");
throw new UnauthorizedException('Unauthorized!');
}
do_log("allow!");
return $next($request);
}
}

View File

@@ -2,6 +2,7 @@
namespace App\Http\Middleware;
use App\Repositories\IpLogRepository;
use Closure;
use Illuminate\Http\Request;
use Nexus\Nexus;

View File

@@ -0,0 +1,26 @@
<?php
namespace App\Http\Middleware;
use App\Repositories\IpLogRepository;
use Closure;
use Illuminate\Http\Request;
use Symfony\Component\HttpFoundation\Response;
class LogUserIp
{
/**
* Handle an incoming request.
*
* @param \Closure(\Illuminate\Http\Request): (\Symfony\Component\HttpFoundation\Response) $next
*/
public function handle(Request $request, Closure $next): Response
{
$response = $next($request);
$user = $request->user();
if ($user) {
IpLogRepository::saveToCache($user->id);
}
return $response;
}
}

View File

@@ -1,30 +0,0 @@
<?php
namespace App\Http\Middleware;
use App\Models\User;
use Closure;
use Illuminate\Http\Request;
use Illuminate\Validation\UnauthorizedException;
class Permission
{
/**
* Handle an incoming request.
*
* @param \Illuminate\Http\Request $request
* @param \Closure $next
* @return mixed
*/
public function handle(Request $request, Closure $next)
{
/** @var CheckUserStatus $user */
$user = $request->user();
if (!$user || (nexus()->isPlatformAdmin() && !$user->canAccessAdmin())) {
do_log("denied!");
throw new UnauthorizedException('Unauthorized!');
}
do_log("allow!");
return $next($request);
}
}

View File

@@ -1,29 +0,0 @@
<?php
namespace App\Http\Middleware;
use Closure;
use Illuminate\Http\Request;
use Illuminate\Validation\UnauthorizedException;
class Platform
{
/**
* Handle an incoming request.
*
* @param \Illuminate\Http\Request $request
* @param \Closure $next
* @return mixed
*/
public function handle(Request $request, Closure $next)
{
$platform = nexus()->getPlatform();
if (empty($platform)) {
throw new \InvalidArgumentException("Require platform header.");
}
if (!nexus()->isPlatformValid()) {
throw new \InvalidArgumentException("Invalid platform: " . $platform);
}
return $next($request);
}
}

View File

@@ -3,8 +3,10 @@
namespace App\Jobs;
use App\Models\BonusLogs;
use App\Models\IpLog;
use App\Models\Setting;
use App\Models\User;
use App\Repositories\IpLogRepository;
use Illuminate\Bus\Queueable;
use Illuminate\Contracts\Queue\ShouldBeUnique;
use Illuminate\Contracts\Queue\ShouldQueue;
@@ -238,7 +240,7 @@ class CalculateUserSeedBonus implements ShouldQueue
$client = app(\ClickHouseDB\Client::class);
$fields = ['business_type', 'uid', 'old_total_value', 'value', 'new_total_value', 'comment', 'created_at'];
$client->insert("bonus_logs", $bonusLogInsert, $fields);
do_log("insertIntoClickHouseBulk done, created_at: {$bonusLogInsert[0]['created_at']}");
do_log("insertIntoClickHouseBulk done, created_at: {$bonusLogInsert[0]['created_at']}, count: " . count($bonusLogInsert));
} catch (\Exception $e) {
do_log($e->getMessage(), 'error');
}

View File

@@ -0,0 +1,31 @@
<?php
namespace App\Jobs;
use App\Repositories\IpLogRepository;
use Illuminate\Contracts\Queue\ShouldQueue;
use Illuminate\Foundation\Queue\Queueable;
class SaveIpLogCacheToDB implements ShouldQueue
{
use Queueable;
public $tries = 1;
/**
* Create a new job instance.
*/
public function __construct()
{
//
}
/**
* Execute the job.
*/
public function handle(): void
{
IpLogRepository::saveToDB();
do_log("done");
}
}

40
app/Models/IpLog.php Normal file
View File

@@ -0,0 +1,40 @@
<?php
namespace App\Models;
use Illuminate\Database\Eloquent\Casts\Attribute;
class IpLog extends NexusModel
{
protected $table = 'iplog';
protected $fillable = ['ip', 'userid', 'access', 'uri', 'count'];
protected function ipLocation(): Attribute
{
return new Attribute(
get: fn (mixed $value, array $attributes) => $this->getIpLocation($attributes['ip'])
);
}
private function getIpLocation(string $ip)
{
$result = get_ip_location_from_geoip($ip);
$out = $result['name'];
$suffix = [];
if (!empty($result['city_en'])) {
$suffix[] = $result['city_en'];
}
if (!empty($result['country_en'])) {
$suffix[] = $result['country_en'];
}
if (!empty($result['continent_en'])) {
$suffix[] = $result['continent_en'];
}
if (!empty($suffix)) {
$out .= " " . implode(', ', $suffix);
}
return $out;
}
}

View File

@@ -2,6 +2,7 @@
namespace App\Models;
use Illuminate\Database\Eloquent\Casts\Attribute;
use Illuminate\Database\Eloquent\Factories\HasFactory;
use Illuminate\Database\Eloquent\Model;
use Nexus\Database\NexusDB;
@@ -16,6 +17,13 @@ class NexusModel extends Model
protected $connection = NexusDB::ELOQUENT_CONNECTION_NAME;
protected function usernameForAdmin(): Attribute
{
return Attribute::make(
get: fn (mixed $value, array $attributes) => username_for_admin($attributes['uid'] ?? $attributes['userid'] ?? $attributes['user_id'])
);
}
/**
*
* @param \DateTimeInterface $date

View File

@@ -108,7 +108,7 @@ class Setting extends NexusModel
{
$redis = NexusDB::redis();
$key = self::USER_TOKEN_PERMISSION_ALLOWED_CACHE_KRY;
$redis->del($key);
$redis->unlink($key);
//must not use cache
if (empty($allowed)) {
$allowed = self::getFromDb("permission.user_token_allowed");

View File

@@ -35,7 +35,7 @@ class TrackerUrl extends NexusModel
{
//添加 id 与 URL 映射
$redis = NexusDB::redis();
$redis->del(self::TRACKER_URL_CACHE_KEY);
$redis->unlink(self::TRACKER_URL_CACHE_KEY);
$list = self::listAll();
$first = $list->first();
$hasDefault = false;

View File

@@ -40,11 +40,11 @@ class RouteServiceProvider extends ServiceProvider
$this->routes(function () {
Route::prefix('api/v1')
->middleware('api')
->middleware(['api', 'locale'])
->namespace($this->namespace)
->group(base_path('routes/api.php'));
Route::middleware('web')
Route::middleware(['web', 'locale'])
->namespace($this->namespace)
->group(base_path('routes/web.php'));

View File

@@ -132,7 +132,7 @@ class CleanupRepository extends BaseRepository
//remove this batch
if ($batchKey != self::USER_SEED_BONUS_BATCH_KEY) {
$redis->del($batch);
$redis->unlink($batch);
}
$endTimestamp = time();
do_log(sprintf("$logPrefix, [DONE], batch: $batch, count: $count, cost time: %d seconds", $endTimestamp - $beginTimestamp));

View File

@@ -0,0 +1,87 @@
<?php
namespace App\Repositories;
use App\Models\IpLog;
use Carbon\Carbon;
use Nexus\Database\NexusDB;
class IpLogRepository extends BaseRepository
{
const CACHE_KEY_PREFIX = 'nexus_ip_logs';
private const CACHE_TIME = 72 * 3600;
public static function saveToCache($userId, $uri = null, $ipArr = null): void
{
if (!is_numeric($userId) || $userId <= 0) {
do_log("invalid userId: $userId", "error");
return;
}
$redis = NexusDB::redis();
if (is_null($uri)) {
$parsed_uri = parse_url($_SERVER['REQUEST_URI']);
$uri = $parsed_uri['path'];
}
if (is_null($ipArr)) {
$ipArr = [getip()];
}
$key = sprintf("%s:%s", self::CACHE_KEY_PREFIX, date('Y-m-d-H'));
foreach ($ipArr as $ip) {
$field = sprintf("%s|%s|%s", $userId, $ip, $uri);
$result = $redis->hincrby($key, $field, 1);
do_log("success hincrby $key $field, result: $result", "debug");
if ($result === 1) {
$redis->expire($key, self::CACHE_TIME);
}
}
}
public static function saveToDB(): void
{
$beginTimestamp = microtime(true);
$redis = NexusDB::redis();
$begin = Carbon::now()->subSeconds(self::CACHE_TIME);
$end = Carbon::now()->subHours(1);
$interval =\DateInterval::createFromDateString("1 hour");
$period = new \DatePeriod($begin->clone(), $interval, $end);
$size = 2000;
do_log(sprintf("begin: %s, end: %s, size: %s", $begin->toDateTimeString(), $end->toDateTimeString(), $size));
$redis->setOption(\Redis::OPT_SCAN, \Redis::SCAN_RETRY);
foreach ($period as $dt) {
$key = sprintf("%s:%s", self::CACHE_KEY_PREFIX, $dt->format('Y-m-d-H'));
if (!$redis->exists($key)) {
do_log("key: $key not found", "debug");
continue;
}
if ($redis->hlen($key) == 0) {
do_log("key: $key length = 0", "debug");
$redis->unlink($key);
}
do_log("handing key: $key");
//遍历hash
$it = NULL;
while($arr_keys = $redis->hScan($key, $it, "*", $size)) {
$insert = [];
foreach ($arr_keys as $field => $value) {
list($userId, $ip, $uri) = explode("|", $field);
$insert[] = [
'userid' => $userId,
'ip' => $ip,
'uri' => $uri,
'access' => date("Y-m-d H:i:s"),
'count' => intval($value),
];
}
if (!empty($insert)) {
IpLog::query()->insert($insert);
}
do_log("key: $key, it: $it, count: " . count($insert));
}
$redis->unlink($key);
do_log("handle key: $key done!");
}
do_log(sprintf("all done! cost time: %.3f sec.", microtime(true) - $beginTimestamp));
}
}

View File

@@ -104,7 +104,7 @@ class RequireSeedTorrentRepository extends BaseRepository
//remove torrent from list
$redis->hDel(self::getTorrentCacheKey(), $torrent->id);
//remove all users under torrent
$redis->del(self::getTorrentUserCacheKey($torrent->id));
$redis->unlink(self::getTorrentUserCacheKey($torrent->id));
}
RequireSeedTorrent::query()->whereIn('torrent_id', $idArr)->delete();
UserRequireSeedTorrent::query()->whereIn('torrent_id', $idArr)->delete();

View File

@@ -221,9 +221,7 @@ class TorrentRepository extends BaseRepository
}
if ($apiQueryBuilder->hasIncludeField('description') && $apiQueryBuilder->hasInclude('extra')) {
do_log("before format_description of torrent: {$torrent->id}");
$descriptionArr = format_description($torrent->extra->descr ?? '');
do_log("after format_description of torrent: {$torrent->id}");
$torrent->description = $descriptionArr;
$torrent->images = get_image_from_description($descriptionArr);
}

View File

@@ -101,10 +101,10 @@ class ApiQueryBuilder
{
$includeCounts = explode(',', $this->request->query(self::PARAM_NAME_INCLUDE_COUNTS, ''));
$valid = array_intersect($this->allowedIncludeCounts, $includeCounts);
do_log(sprintf(
"includeCounts: %s, allow: %s, valid: %s",
json_encode($includeCounts), json_encode($this->allowedIncludeCounts), json_encode($valid)
));
// do_log(sprintf(
// "includeCounts: %s, allow: %s, valid: %s",
// json_encode($includeCounts), json_encode($this->allowedIncludeCounts), json_encode($valid)
// ));
$this->query->withCount($valid);
}

View File

@@ -0,0 +1,30 @@
<?php
use Illuminate\Database\Migrations\Migration;
use Illuminate\Database\Schema\Blueprint;
use Illuminate\Support\Facades\Schema;
return new class extends Migration
{
/**
* Run the migrations.
*/
public function up(): void
{
Schema::table('iplog', function (Blueprint $table) {
$table->string('uri')->nullable();
$table->integer('count')->default(0);
$table->index('ip');
});
}
/**
* Reverse the migrations.
*/
public function down(): void
{
Schema::table('iplog', function (Blueprint $table) {
$table->dropColumn(['uri', 'count']);
});
}
};

View File

@@ -2498,10 +2498,12 @@ function stdhead($title = "", $msgalert = true, $script = "", $place = "")
$tstart = getmicrotime(); // Start time
//Insert old ip into iplog
if ($CURUSER){
if ($iplog1 == "yes") {
if (($oldip != $CURUSER["ip"]) && $CURUSER["ip"])
sql_query("INSERT INTO iplog (ip, userid, access) VALUES (" . sqlesc($CURUSER['ip']) . ", " . $CURUSER['id'] . ", '" . $CURUSER['last_access'] . "')");
}
// if ($iplog1 == "yes") {
// if (($oldip != $CURUSER["ip"]) && $CURUSER["ip"])
// sql_query("INSERT INTO iplog (ip, userid, access) VALUES (" . sqlesc($CURUSER['ip']) . ", " . $CURUSER['id'] . ", '" . $CURUSER['last_access'] . "')");
// }
//record always
\App\Repositories\IpLogRepository::saveToCache($CURUSER['id']);
$USERUPDATESET[] = "last_access = ".sqlesc(date("Y-m-d H:i:s"));
$USERUPDATESET[] = "ip = ".sqlesc($CURUSER['ip']);
}
@@ -2966,8 +2968,10 @@ function stdfoot() {
echo "<div align=\"center\" style=\"margin-top: 10px\" id=\"\">".$footerad[0]."</div>";
}
print("<div style=\"margin-top: 10px; margin-bottom: 30px;\" align=\"center\">");
if ($CURUSER && count($USERUPDATESET)){
sql_query("UPDATE users SET " . join(",", $USERUPDATESET) . " WHERE id = ".$CURUSER['id']);
if ($CURUSER) {
if (count($USERUPDATESET)) {
sql_query("UPDATE users SET " . join(",", $USERUPDATESET) . " WHERE id = ".$CURUSER['id']);
}
}
// Variables for End Time
$tend = microtime(true);
@@ -6099,9 +6103,9 @@ function calculate_seed_bonus($uid, $torrentIdArr = null): array
$torrentIdArr = [-1];
}
$idStr = implode(',', \Illuminate\Support\Arr::wrap($torrentIdArr));
$sql = "select torrents.id, torrents.added, torrents.size, torrents.seeders, 'NO_PEER_ID' as peerID, '' as last_action from torrents WHERE id in ($idStr) and size >= $minSize";
$sql = "select torrents.id, torrents.added, torrents.size, torrents.seeders, 'NO_PEER_ID' as peerID, '' as last_action, '' as ip from torrents WHERE id in ($idStr) and size >= $minSize";
} else {
$sql = "select torrents.id, torrents.added, torrents.size, torrents.seeders, peers.id as peerID, peers.last_action from torrents LEFT JOIN peers ON peers.torrent = torrents.id WHERE peers.userid = $uid AND peers.seeder ='yes' and torrents.size > $minSize group by peers.torrent, peers.peer_id";
$sql = "select torrents.id, torrents.added, torrents.size, torrents.seeders, peers.id as peerID, peers.last_action, peers.ip from torrents LEFT JOIN peers ON peers.torrent = torrents.id WHERE peers.userid = $uid AND peers.seeder ='yes' and torrents.size > $minSize group by peers.torrent, peers.peer_id";
}
$tagGrouped = [];
$torrentResult = \Nexus\Database\NexusDB::select($sql);
@@ -6120,11 +6124,15 @@ function calculate_seed_bonus($uid, $torrentIdArr = null): array
$medalAdditionalFactor = floatval($userMedalResult[0]['factor'] ?? 0);
do_log("$logPrefix, sql: $sql, count: " . count($torrentResult) . ", officialTag: $officialTag, officialAdditionalFactor: $officialAdditionalFactor, zeroBonusTag: $zeroBonusTag, zeroBonusFactor: $zeroBonusFactor, medalAdditionalFactor: $medalAdditionalFactor");
$last_action = "";
$ip_arr = [];
foreach ($torrentResult as $torrent)
{
if ($torrent['last_action'] > $last_action) {
$last_action = $torrent['last_action'];
}
if (!empty($torrent['ip']) && !isset($ip_arr[$torrent['ip']])) {
$ip_arr[$torrent['ip']] = $torrent['ip'];
}
$size = bcadd($size, $torrent['size']);
$weeks_alive = ($timenow - strtotime($torrent['added'])) / $sectoweek;
$gb_size = $gb_size_raw = $torrent['size'] / 1073741824;
@@ -6155,11 +6163,12 @@ function calculate_seed_bonus($uid, $torrentIdArr = null): array
$medal_bonus = $valuetwo * atan($A / $l_bonus);
$result = compact(
'seed_points','seed_bonus', 'A', 'count', 'torrent_peer_count', 'size', 'last_action',
'official_bonus', 'official_a', 'official_torrent_peer_count', 'official_size', 'medal_bonus'
'official_bonus', 'official_a', 'official_torrent_peer_count', 'official_size', 'medal_bonus',
);
$result['donor_times'] = $donortimes_bonus;
$result['official_additional_factor'] = $officialAdditionalFactor;
$result['medal_additional_factor'] = $medalAdditionalFactor;
$result['ip_arr'] = array_keys($ip_arr);
do_log("$logPrefix, result: " . json_encode($result));
return $result;
}
@@ -6514,6 +6523,9 @@ function torrent_name_for_admin(\App\Models\Torrent|null $torrent, $withTags = f
function username_for_admin(int $id)
{
if (empty($id)) {
return '';
}
return new HtmlString(get_username($id, false, true, true, true));
}

View File

@@ -53,13 +53,7 @@ function benc_resp($d)
function benc_resp_raw($x) {
header("Content-Type: text/plain; charset=utf-8");
header("Pragma: no-cache");
if (isset($_SERVER["HTTP_ACCEPT_ENCODING"]) && $_SERVER["HTTP_ACCEPT_ENCODING"] == "gzip" && function_exists('gzencode')) {
header("Content-Encoding: gzip");
echo gzencode($x, 9, FORCE_GZIP);
}
else
echo $x;
echo $x;
}
/**

View File

@@ -731,7 +731,7 @@ class Install
$redis = NexusDB::redis();
$result = $redis->info();
$version = $result['redis_version'];
$minVersion = '2.6.12';
$minVersion = '4.0.0';
$match = version_compare($version, $minVersion, '>=');
return compact('version', 'match', 'minVersion');
}

View File

@@ -657,6 +657,7 @@ if(count($USERUPDATESET) && $userid)
$lockKey = sprintf("record_batch_lock:%s:%s", $userid, $torrentid);
if ($redis->set($lockKey, TIMENOW, ['nx', 'ex' => $autoclean_interval_one])) {
\App\Repositories\CleanupRepository::recordBatch($redis, $userid, $torrentid);
\App\Repositories\IpLogRepository::saveToCache($userid, null, [$ip]);
}
if (\App\Repositories\RequireSeedTorrentRepository::shouldRecordUser($redis, $userid, $torrentid)) {
if (!isset($snatchInfo)) {

View File

@@ -62,10 +62,11 @@ if (!empty($_REQUEST['downhash'])) {
}
}
//User may choose to download torrent from RSS. So log ip changes when downloading torrents.
if ($iplog1 == "yes") {
if (($oldip != $CURUSER["ip"]) && $CURUSER["ip"])
sql_query("INSERT INTO iplog (ip, userid, access) VALUES (" . sqlesc($CURUSER['ip']) . ", " . $CURUSER['id'] . ", '" . $CURUSER['last_access'] . "')");
}
//if ($iplog1 == "yes") {
// if (($oldip != $CURUSER["ip"]) && $CURUSER["ip"])
// sql_query("INSERT INTO iplog (ip, userid, access) VALUES (" . sqlesc($CURUSER['ip']) . ", " . $CURUSER['id'] . ", '" . $CURUSER['last_access'] . "')");
//}
\App\Repositories\IpLogRepository::saveToCache($CURUSER['id']);
//User may choose to download torrent from RSS. So update his last_access and ip when downloading torrents.
sql_query("UPDATE users SET last_access = ".sqlesc(date("Y-m-d H:i:s")).", ip = ".sqlesc($CURUSER['ip'])." WHERE id = ".sqlesc($CURUSER['id']));

View File

@@ -6,6 +6,33 @@ dbconn_announce();
// BLOCK ACCESS WITH WEB BROWSERS AND CHEATS!
block_browser();
$passkey = $_GET['passkey'] ?? '';
if (empty($passkey)) {
err('require passkey');
}
$redis = $Cache->getRedis();
$passkeyInvalidKey = "passkey_invalid";
// check passkey
if (!$az = $Cache->get_value('user_passkey_'.$passkey.'_content')){
$res = sql_query("SELECT id, username, downloadpos, enabled, uploaded, downloaded, class, parked, clientselect, showclienterror, passkey, donor, donoruntil, seedbonus, tracker_url_id FROM users WHERE passkey=". sqlesc($passkey)." LIMIT 1");
$az = mysql_fetch_array($res);
do_log("[check passkey], currentUser: " . nexus_json_encode($az));
$Cache->cache_value('user_passkey_'.$passkey.'_content', $az, 3600);
}
if (!$az) {
$redis->set("$passkeyInvalidKey:$passkey", TIMENOW, ['ex' => 24*3600]);
err("Invalid passkey! Re-download the .torrent from $BASEURL");
}
if ($az["enabled"] == "no")
err("Your account is disabled!", 300);
elseif ($az["parked"] == "yes")
err("Your account is parked! (Read the FAQ)", 300);
elseif ($az["downloadpos"] == "no")
err("Your downloading privileges have been disabled! (Read the rules)", 300);
$userid = intval($az['id'] ?? 0);
unset($GLOBALS['CURUSER']);
$CURUSER = $GLOBALS["CURUSER"] = $az;
preg_match_all('/info_hash=([^&]*)/i', $_SERVER["QUERY_STRING"], $info_hash_array);
$fields = "info_hash, times_completed, seeders, leechers";

View File

@@ -310,7 +310,7 @@ elseif ($action == 'tweaksettings') // tweak settings
print ($notice);
print ("<form method='post' action='".$_SERVER["SCRIPT_NAME"]."'><input type='hidden' name='action' value='savesettings_tweak' />");
yesorno($lang_settings['row_save_user_location'], 'where', $TWEAK["where"], $lang_settings['text_save_user_location_note']);
yesorno($lang_settings['row_log_user_ips'], 'iplog1', $TWEAK["iplog1"], $lang_settings['text_store_user_ips_note']);
// yesorno($lang_settings['row_log_user_ips'], 'iplog1', $TWEAK["iplog1"], $lang_settings['text_store_user_ips_note']);
tr($lang_settings['row_kps_enabled'],"<input type='radio' id='bonusenable' name='bonus'" . ($TWEAK["bonus"] == "enable" ? " checked='checked'" : "") . " value='enable' /> <label for='bonusenable'>".$lang_settings['text_enabled']."</label> <input type='radio' id='bonusdisablesave' name='bonus'" . ($TWEAK["bonus"] == "disablesave" ? " checked='checked'" : "") . " value='disablesave' /> <label for='bonusdisablesave'>".$lang_settings['text_disabled_but_save']."</label> <input type='radio' id='bonusdisable' name='bonus'" . ($TWEAK["bonus"] == "disable" ? " checked='checked'" : "") . " value='disable' /> <label for='bonusdisable'>".$lang_settings['text_disabled_no_save']."</label> <br />".$lang_settings['text_kps_note'], 1);
yesorno($lang_settings['row_enable_location'], 'enablelocation', $TWEAK["enablelocation"], $lang_settings['text_enable_location_note']);
yesorno($lang_settings['row_enable_tooltip'], 'enabletooltip', $TWEAK["enabletooltip"], $lang_settings['text_enable_tooltip_note']);

View File

@@ -0,0 +1,12 @@
<?php
return [
'label' => 'IP History',
'access' => 'Access time',
'access_begin' => 'Access time begin',
'access_end' => 'Access time end',
'access_tooltip' => 'The actual time was within one hour prior to this.',
'uri' => 'URI',
'count' => 'Count',
'ip_location' => 'Location',
];

View File

@@ -0,0 +1,12 @@
<?php
return [
'label' => 'IP 历史',
'access' => '访问时间',
'access_begin' => '访问时间开始',
'access_end' => '访问时间结束',
'access_tooltip' => '实际时间在此之前 1 小时内',
'uri' => '访问路径',
'count' => '访问次数',
'ip_location' => '地理位置',
];

View File

@@ -0,0 +1,12 @@
<?php
return [
'label' => 'IP 歷史',
'access' => '訪問時間',
'access_begin' => '訪問時間開始',
'access_end' => '訪問時間結束',
'access_tooltip' => '實際時間在此之前 1 小時內',
'uri' => '訪問路徑',
'count' => '訪問次數',
'ip_location' => '地理位置',
];