mirror of
https://github.com/lkddi/nexusphp.git
synced 2026-04-21 02:20:54 +08:00
refactor isSeedBox judgement
This commit is contained in:
@@ -1,22 +1,30 @@
|
||||
<?php
|
||||
namespace App\Repositories;
|
||||
|
||||
use App\Events\SeedBoxRecordUpdated;
|
||||
use App\Enums\SeedBoxRecord\IpAsnEnum;
|
||||
use App\Enums\SeedBoxRecord\IsAllowedEnum;
|
||||
use App\Enums\SeedBoxRecord\TypeEnum;
|
||||
use App\Exceptions\InsufficientPermissionException;
|
||||
use App\Models\Message;
|
||||
use App\Models\Poll;
|
||||
use App\Models\SeedBoxRecord;
|
||||
use App\Models\Torrent;
|
||||
use App\Models\User;
|
||||
use GeoIp2\Database\Reader;
|
||||
use Illuminate\Support\Arr;
|
||||
use Illuminate\Support\Facades\Auth;
|
||||
use Illuminate\Support\Facades\DB;
|
||||
use MaxMind\Db\Reader\InvalidDatabaseException;
|
||||
use Nexus\Database\NexusDB;
|
||||
use PhpIP\IP;
|
||||
use PhpIP\IPBlock;
|
||||
|
||||
class SeedBoxRepository extends BaseRepository
|
||||
{
|
||||
const IS_SEED_BOX_FROM_USER_RECORD_CACHE_PREFIX = "IS_SEED_BOX_FROM_USER_RECORD";
|
||||
|
||||
const APPROVAL_COUNT_CACHE_KEY = "SEED_BOX_RECORD_APPROVAL_NONE";
|
||||
|
||||
private static ?Reader $asnReader = null;
|
||||
|
||||
public function getList(array $params)
|
||||
{
|
||||
$query = Poll::query();
|
||||
@@ -33,7 +41,7 @@ class SeedBoxRepository extends BaseRepository
|
||||
{
|
||||
$params = $this->formatParams($params);
|
||||
$seedBoxRecord = SeedBoxRecord::query()->create($params);
|
||||
$this->clearCache();
|
||||
$this->clearApprovalCountCache();
|
||||
publish_model_event("seed_box_record_created", $seedBoxRecord->id);
|
||||
return $seedBoxRecord;
|
||||
}
|
||||
@@ -102,7 +110,7 @@ class SeedBoxRepository extends BaseRepository
|
||||
$model = SeedBoxRecord::query()->findOrFail($id);
|
||||
$params = $this->formatParams($params);
|
||||
$model->update($params);
|
||||
$this->clearCache();
|
||||
$this->clearApprovalCountCache();
|
||||
publish_model_event("seed_box_record_updated", $id);
|
||||
return $model;
|
||||
}
|
||||
@@ -115,7 +123,7 @@ class SeedBoxRepository extends BaseRepository
|
||||
|
||||
public function delete($id, $uid)
|
||||
{
|
||||
$this->clearCache();
|
||||
$this->clearApprovalCountCache();
|
||||
publish_model_event("seed_box_record_deleted", $id);
|
||||
return SeedBoxRecord::query()->whereIn('id', Arr::wrap($id))->where('uid', $uid)->delete();
|
||||
}
|
||||
@@ -146,7 +154,7 @@ class SeedBoxRepository extends BaseRepository
|
||||
return NexusDB::transaction(function () use ($seedBoxRecord, $status, $message) {
|
||||
$seedBoxRecord->status = $status;
|
||||
$seedBoxRecord->save();
|
||||
$this->clearCache();
|
||||
$this->clearApprovalCountCache();
|
||||
return Message::add($message);
|
||||
});
|
||||
}
|
||||
@@ -173,12 +181,194 @@ class SeedBoxRepository extends BaseRepository
|
||||
return '<img src="pic/misc/seed-box.png" style="vertical-align: bottom; height: 16px; margin-left: 4px" title="SeedBox" />';
|
||||
}
|
||||
|
||||
private function clearCache()
|
||||
private function clearApprovalCountCache(): void
|
||||
{
|
||||
NexusDB::cache_del('SEED_BOX_RECORD_APPROVAL_NONE');
|
||||
// SeedBoxRecordUpdated::dispatch();
|
||||
NexusDB::cache_del(self::APPROVAL_COUNT_CACHE_KEY);
|
||||
}
|
||||
|
||||
public function updateUserCacheCronjob(IsAllowedEnum $isAllowed, IpAsnEnum $field): void
|
||||
{
|
||||
$size = 1000;
|
||||
$page = 1;
|
||||
$logPrefix = "isAllowed: $isAllowed->name, field: $field->name, page: $page, size: $size";
|
||||
$selectRaw = sprintf("uid, group_concat(%s) as str", $field == IpAsnEnum::ASN ? 'asn' : 'ip');
|
||||
while (true) {
|
||||
$list = SeedBoxRecord::getValidQuery(TypeEnum::USER, $isAllowed, $field)
|
||||
->selectRaw($selectRaw)
|
||||
->groupBy('uid')
|
||||
->forPage($page, $size)
|
||||
->get();
|
||||
if ($list->isEmpty()) {
|
||||
do_log("$logPrefix, no more data ...");
|
||||
break;
|
||||
}
|
||||
foreach ($list as $record) {
|
||||
$uid = $record->uid;
|
||||
$str = $record->str;
|
||||
do_log("$logPrefix, handling user: $uid with $field->name: $str");
|
||||
self::updateCache($record->uid, TypeEnum::USER, $isAllowed, $field, $str);
|
||||
do_log("$logPrefix, handling user: $uid with $field->name: $str done!");
|
||||
}
|
||||
$page++;
|
||||
}
|
||||
do_log("$logPrefix, all done!");
|
||||
}
|
||||
|
||||
public function updateAdminCacheCronjob(IsAllowedEnum $isAllowed, IpAsnEnum $field): void
|
||||
{
|
||||
$size = 1000;
|
||||
$page = 1;
|
||||
$logPrefix = "isAllowed: $isAllowed->name, field: $field->name, page: $page, size: $size";
|
||||
$fieldName = $field == IpAsnEnum::ASN ? 'asn' : 'ip';
|
||||
while (true) {
|
||||
$list = SeedBoxRecord::getValidQuery(TypeEnum::ADMIN, $isAllowed, $field)
|
||||
->selectRaw($fieldName)
|
||||
->forPage($page, $size)
|
||||
->get();
|
||||
if ($list->isEmpty()) {
|
||||
do_log("$logPrefix, no more data ...");
|
||||
break;
|
||||
}
|
||||
self::updateCache(0, TypeEnum::ADMIN, $isAllowed, $field, $list->pluck($fieldName)->join(","));
|
||||
$page++;
|
||||
}
|
||||
do_log("$logPrefix, all done!");
|
||||
}
|
||||
|
||||
public static function updateCache(int $userId, TypeEnum $type, IsAllowedEnum $isAllowed, IpAsnEnum $field, string $ipOrAsnStr = null): void
|
||||
{
|
||||
if (!is_null($ipOrAsnStr)) {
|
||||
$list = explode(',', $ipOrAsnStr);
|
||||
} else {
|
||||
$query = SeedBoxRecord::getValidQuery($type, $isAllowed, $field);
|
||||
if ($userId > 0) {
|
||||
$query->where('uid', $userId);
|
||||
}
|
||||
if ($field == IpAsnEnum::IP) {
|
||||
$list = $query->pluck('ip')->toArray();
|
||||
} else {
|
||||
$list = $query->pluck('asn')->toArray();
|
||||
}
|
||||
}
|
||||
$list = array_filter($list);
|
||||
$key = self::getCacheKey($userId, $isAllowed, $field);
|
||||
do_log("userId: $userId, type: $type->name, isAllowed: $isAllowed->name, ipOrAsn: $field->name, key: $key, list: " . json_encode($list));
|
||||
NexusDB::cache_del($key);
|
||||
if (!empty($list)) {
|
||||
NexusDB::redis()->sadd($key, ...$list);
|
||||
NexusDB::redis()->expireAt($key, time() + 86400 * 30);
|
||||
}
|
||||
}
|
||||
|
||||
public static function isSeedBoxFromUserRecords(int $userId, string $ip): array
|
||||
{
|
||||
$logPrefix = "userId: $userId, ip: $ip";
|
||||
$redis = NexusDB::redis();
|
||||
//first check from asn field
|
||||
$asn = self::getAsnFromIp($ip);
|
||||
$uidArr = [0, $userId];
|
||||
if ($asn > 0) {
|
||||
//check if allowed by asn
|
||||
$logPrefix .= ", asn: $asn";
|
||||
foreach($uidArr as $uid) {
|
||||
$key = self::getCacheKey($uid, IsAllowedEnum::YES, IpAsnEnum::ASN);
|
||||
if ($redis->sismember($key, $asn)) {
|
||||
$desc = "$logPrefix, asn $asn in allowed $key, result: false";
|
||||
return self::buildCheckResult(false, $desc);
|
||||
}
|
||||
}
|
||||
foreach($uidArr as $uid) {
|
||||
$key = self::getCacheKey($uid, IsAllowedEnum::NO, IpAsnEnum::ASN);
|
||||
if ($redis->sismember($key, $asn)) {
|
||||
$desc = ("$logPrefix, asn $asn in $key, result: true");
|
||||
return self::buildCheckResult(true, $desc);
|
||||
}
|
||||
}
|
||||
}
|
||||
//then check from ip field
|
||||
foreach($uidArr as $uid) {
|
||||
$key = self::getCacheKey($uid, IsAllowedEnum::YES, IpAsnEnum::IP);
|
||||
if ($redis->sismember($key, $ip)) {
|
||||
$desc = ("$logPrefix, ip $ip in allowed $key, result: false");
|
||||
return self::buildCheckResult(false, $desc);
|
||||
}
|
||||
}
|
||||
foreach($uidArr as $uid) {
|
||||
$key = self::getCacheKey($uid, IsAllowedEnum::NO, IpAsnEnum::IP);
|
||||
if ($redis->sismember($key, $ip)) {
|
||||
$desc = ("$logPrefix, ip $ip in $key, result: true");
|
||||
return self::buildCheckResult(true, $desc);
|
||||
}
|
||||
}
|
||||
return self::buildCheckResult(false, "not match any record, result: false");
|
||||
}
|
||||
|
||||
private static function buildCheckResult(bool $isSeedBox, string $desc): array
|
||||
{
|
||||
$result = [
|
||||
'result' => $isSeedBox,
|
||||
'desc' => $desc,
|
||||
];
|
||||
do_log(json_encode($result));
|
||||
return $result;
|
||||
}
|
||||
|
||||
public static function getAsnFromIp(string $ip): int
|
||||
{
|
||||
//虽然 ip 对应的 asn 相对固定,但不宜设置较大的缓存时间,IP 地址较多,容易引起内存膨胀
|
||||
return NexusDB::remember("IP_TO_ASN:$ip", 3600, function () use ($ip) {
|
||||
$reader = self::getAsnReader();
|
||||
if (is_null($reader)) {
|
||||
return 0;
|
||||
}
|
||||
$asnObj = $reader->asn($ip);
|
||||
return $asnObj->autonomousSystemNumber ?? 0;
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* IS_SEED_BOX_FROM_USER_RECORD:IP:INCLUDES:USER:10001
|
||||
* IS_SEED_BOX_FROM_USER_RECORD:IP:EXCLUDES:USER:10001
|
||||
* IS_SEED_BOX_FROM_USER_RECORD:ASN:INCLUDES:USER:10001
|
||||
* IS_SEED_BOX_FROM_USER_RECORD:ASN:EXCLUDES:USER:10001
|
||||
* IS_SEED_BOX_FROM_USER_RECORD:ASN:EXCLUDES:ADMIN
|
||||
*
|
||||
* @param int $userId
|
||||
* @param int $isAllowed
|
||||
* @param string $field
|
||||
* @return string
|
||||
*/
|
||||
private static function getCacheKey(int $userId, IsAllowedEnum $isAllowed, IpAsnEnum $field): string
|
||||
{
|
||||
$key = sprintf(
|
||||
"%s:%s:%s",
|
||||
self::IS_SEED_BOX_FROM_USER_RECORD_CACHE_PREFIX,
|
||||
$field->name,
|
||||
$isAllowed == IsAllowedEnum::YES ? "EXCLUDES" : "INCLUDES", //允许,要排队它不是 seedBox
|
||||
);
|
||||
if ($userId > 0) {
|
||||
$key .= ":USER:$userId";
|
||||
} else {
|
||||
$key .= ":ADMIN";
|
||||
}
|
||||
return $key;
|
||||
}
|
||||
|
||||
/**
|
||||
* @throws InvalidDatabaseException
|
||||
*/
|
||||
private static function getAsnReader(): ?Reader
|
||||
{
|
||||
if (is_null(self::$asnReader)) {
|
||||
$database = nexus_env('GEOIP2_ASN_DATABASE');
|
||||
if (!file_exists($database) || !is_readable($database)) {
|
||||
do_log("GEOIP2_ASN_DATABASE: $database not exists or not readable", "debug");
|
||||
return null;
|
||||
}
|
||||
self::$asnReader = new Reader($database);
|
||||
}
|
||||
return self::$asnReader;
|
||||
}
|
||||
|
||||
|
||||
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user