diff --git a/.env.example b/.env.example index 05707f5c..1f955647 100644 --- a/.env.example +++ b/.env.example @@ -62,6 +62,7 @@ GOOGLE_DRIVE_REFRESH_TOKEN= GOOGLE_DRIVE_FOLDER_ID= GEOIP2_DATABASE= +GEOIP2_ASN_DATABASE= ELASTICSEARCH_HOST= ELASTICSEARCH_PORT= diff --git a/app/Console/Commands/Test.php b/app/Console/Commands/Test.php index dcc33d94..85483c6c 100644 --- a/app/Console/Commands/Test.php +++ b/app/Console/Commands/Test.php @@ -101,8 +101,12 @@ class Test extends Command */ public function handle() { - $result = \Nexus\Plugin\Plugin::listEnabled(); - dd($result); + $file = resource_path("geoip/GeoLite2-ASN.mmdb"); + $file = resource_path("geoip/GeoLite2-City.mmdb"); + $reader = new Reader($file); + $asn = $reader->asn("94.16.120.0"); +// $result = \Nexus\Plugin\Plugin::listEnabled(); + dd($asn); } } diff --git a/app/Filament/Resources/System/SeedBoxRecordResource.php b/app/Filament/Resources/System/SeedBoxRecordResource.php index e48f1086..54c02b8d 100644 --- a/app/Filament/Resources/System/SeedBoxRecordResource.php +++ b/app/Filament/Resources/System/SeedBoxRecordResource.php @@ -47,6 +47,7 @@ class SeedBoxRecordResource extends Resource ->schema([ Forms\Components\TextInput::make('operator')->label(__('label.seed_box_record.operator')), Forms\Components\TextInput::make('bandwidth')->label(__('label.seed_box_record.bandwidth'))->integer(), + Forms\Components\TextInput::make('asn')->label(__('label.seed_box_record.asn'))->integer(), Forms\Components\TextInput::make('ip_begin')->label(__('label.seed_box_record.ip_begin')), Forms\Components\TextInput::make('ip_end')->label(__('label.seed_box_record.ip_end')), Forms\Components\TextInput::make('ip')->label(__('label.seed_box_record.ip'))->helperText(__('label.seed_box_record.ip_help')), @@ -72,6 +73,7 @@ class SeedBoxRecordResource extends Resource , Tables\Columns\TextColumn::make('operator')->label(__('label.seed_box_record.operator'))->searchable(), Tables\Columns\TextColumn::make('bandwidth')->label(__('label.seed_box_record.bandwidth')), + Tables\Columns\TextColumn::make('asn')->label(__('label.seed_box_record.asn')), Tables\Columns\TextColumn::make('ip') ->label(__('label.seed_box_record.ip')) ->searchable(true, function (Builder $query, $search) { diff --git a/app/Jobs/CalculateUserSeedBonus.php b/app/Jobs/CalculateUserSeedBonus.php index 4d8bce1e..eff77bd9 100644 --- a/app/Jobs/CalculateUserSeedBonus.php +++ b/app/Jobs/CalculateUserSeedBonus.php @@ -42,16 +42,6 @@ class CalculateUserSeedBonus implements ShouldQueue $this->requestId = $requestId; } - /** - * Determine the time at which the job should timeout. - * - * @return \DateTime - */ - public function retryUntil() - { - return now()->addSeconds(Setting::get('main.autoclean_interval_one')); - } - public $tries = 1; public $timeout = 3600; diff --git a/app/Jobs/GenerateTemporaryInvite.php b/app/Jobs/GenerateTemporaryInvite.php index ff1fa2db..5be6ee04 100644 --- a/app/Jobs/GenerateTemporaryInvite.php +++ b/app/Jobs/GenerateTemporaryInvite.php @@ -36,16 +36,6 @@ class GenerateTemporaryInvite implements ShouldQueue $this->count = $count; } - /** - * Determine the time at which the job should timeout. - * - * @return \DateTime - */ - public function retryUntil() - { - return now()->addHours(1); - } - public $tries = 1; public $timeout = 1800; diff --git a/app/Jobs/UpdateTorrentSeedersEtc.php b/app/Jobs/UpdateTorrentSeedersEtc.php index 6068bc78..a080a78d 100644 --- a/app/Jobs/UpdateTorrentSeedersEtc.php +++ b/app/Jobs/UpdateTorrentSeedersEtc.php @@ -42,16 +42,6 @@ class UpdateTorrentSeedersEtc implements ShouldQueue $this->requestId = $requestId; } - /** - * Determine the time at which the job should timeout. - * - * @return \DateTime - */ - public function retryUntil() - { - return now()->addSeconds(Setting::get('main.autoclean_interval_three')); - } - public $tries = 1; public $timeout = 1800; diff --git a/app/Jobs/UpdateUserSeedingLeechingTime.php b/app/Jobs/UpdateUserSeedingLeechingTime.php index cc3cf5be..c9c90094 100644 --- a/app/Jobs/UpdateUserSeedingLeechingTime.php +++ b/app/Jobs/UpdateUserSeedingLeechingTime.php @@ -42,16 +42,6 @@ class UpdateUserSeedingLeechingTime implements ShouldQueue $this->requestId = $requestId; } - /** - * Determine the time at which the job should timeout. - * - * @return \DateTime - */ - public function retryUntil() - { - return now()->addSeconds(Setting::get('main.autoclean_interval_four')); - } - public $tries = 1; public $timeout = 3600; @@ -102,8 +92,8 @@ class UpdateUserSeedingLeechingTime implements ShouldQueue $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); + $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)", diff --git a/app/Models/SeedBoxRecord.php b/app/Models/SeedBoxRecord.php index a6b08632..ab756ebb 100644 --- a/app/Models/SeedBoxRecord.php +++ b/app/Models/SeedBoxRecord.php @@ -7,7 +7,7 @@ use Illuminate\Database\Eloquent\Casts\Attribute; class SeedBoxRecord extends NexusModel { protected $fillable = ['type', 'uid', 'status', 'operator', 'bandwidth', 'ip', 'ip_begin', 'ip_end', 'ip_begin_numeric', 'ip_end_numeric', - 'comment', 'version', 'is_allowed', + 'comment', 'version', 'is_allowed', 'asn' ]; public $timestamps = true; diff --git a/app/Repositories/SeedBoxRepository.php b/app/Repositories/SeedBoxRepository.php index 052c6154..487dc50f 100644 --- a/app/Repositories/SeedBoxRepository.php +++ b/app/Repositories/SeedBoxRepository.php @@ -40,7 +40,13 @@ class SeedBoxRepository extends BaseRepository private function formatParams(array $params): array { - if (!empty($params['ip']) && empty($params['ip_begin']) && empty($params['ip_end'])) { + $params = array_filter($params); + if ( + !empty($params['ip']) + && empty($params['ip_begin']) + && empty($params['ip_end']) + && empty($params['asn']) + ) { try { $ipBlock = IPBlock::create($params['ip']); $params['ip_begin_numeric'] = $ipBlock->getFirstIp()->numeric(); @@ -62,8 +68,12 @@ class SeedBoxRepository extends BaseRepository if (empty($params['version'])) { throw new \InvalidArgumentException("Invalid IPBlock or IP: " . $params['ip']); } - - } elseif (empty($params['ip']) && !empty($params['ip_begin']) && !empty($params['ip_end'])) { + } elseif ( + empty($params['ip']) + && empty($params['asn']) + && !empty($params['ip_begin']) + && !empty($params['ip_end']) + ) { $ipBegin = IP::create($params['ip_begin']); $params['ip_begin_numeric'] = $ipBegin->numeric(); @@ -73,6 +83,13 @@ class SeedBoxRepository extends BaseRepository throw new \InvalidArgumentException("ip_begin/ip_end must be the same version"); } $params['version'] = $ipEnd->getVersion(); + } elseif ( + !empty($params['asn']) + && empty($params['ip']) + && empty($params['ip_begin']) + && empty($params['ip_end']) + ) { + do_log("only asn: " . $params['asn']); } else { throw new \InvalidArgumentException(nexus_trans('label.seed_box_record.ip_help')); } diff --git a/database/migrations/2024_11_18_160647_add_asn_to_seed_box_records_table.php b/database/migrations/2024_11_18_160647_add_asn_to_seed_box_records_table.php new file mode 100644 index 00000000..46169b09 --- /dev/null +++ b/database/migrations/2024_11_18_160647_add_asn_to_seed_box_records_table.php @@ -0,0 +1,32 @@ +integer("asn")->default(0)->index(); + }); + } + + /** + * Reverse the migrations. + * + * @return void + */ + public function down() + { + Schema::table('seed_box_records', function (Blueprint $table) { + $table->dropColumn("asn"); + }); + } +}; diff --git a/include/globalfunctions.php b/include/globalfunctions.php index e3e35466..396b5826 100644 --- a/include/globalfunctions.php +++ b/include/globalfunctions.php @@ -857,6 +857,31 @@ function do_action($name, ...$args) return $hook->doAction(...func_get_args()); } +function isIPSeedBoxFromASN($ip): bool +{ + try { + static $reader; + $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 false; + } + if (is_null($reader)) { + $reader = new \GeoIp2\Database\Reader($database); + } + $asnObj = $reader->asn($ip); + $asn = $asnObj->autonomousSystemNumber; + if ($asn <= 0) { + return false; + } + $row = \Nexus\Database\NexusDB::getOne("seed_box_records", "asn = $asn", "id"); + return !empty($row); + } catch (\Throwable $throwable) { + do_log("ip: $ip, error: " . $throwable->getMessage(), "error"); + return false; + } +} + function isIPSeedBox($ip, $uid, $withoutCache = false): bool { $key = "nexus_is_ip_seed_box:ip:$ip:uid:$uid"; @@ -865,22 +890,30 @@ function isIPSeedBox($ip, $uid, $withoutCache = false): bool do_log("$key, get result from cache: $cacheData(" . gettype($cacheData) . ")"); return (bool)$cacheData; } + //check from asn + $res = isIPSeedBoxFromASN($ip); + if (!empty($res)) { + \Nexus\Database\NexusDB::cache_put($key, 1, 300); + do_log("$key, get result from asn, true"); + return true; + } + $ipObject = \PhpIP\IP::create($ip); $ipNumeric = $ipObject->numeric(); $ipVersion = $ipObject->getVersion(); //check allow list first, not consider specific user $checkSeedBoxAllowedSql = sprintf( - 'select id from seed_box_records where `ip_begin_numeric` <= "%s" and `ip_end_numeric` >= "%s" and `version` = %s and `status` = %s and `is_allowed` = 1 limit 1', + 'select id from seed_box_records where `ip_begin_numeric` <= "%s" and `ip_end_numeric` >= "%s" and `version` = %s and `status` = %s and `is_allowed` = 1 and asn = 0 limit 1', $ipNumeric, $ipNumeric, $ipVersion, \App\Models\SeedBoxRecord::STATUS_ALLOWED ); $res = \Nexus\Database\NexusDB::select($checkSeedBoxAllowedSql); if (!empty($res)) { - \Nexus\Database\NexusDB::cache_put($key, 1, 300); + \Nexus\Database\NexusDB::cache_put($key, 0, 300); do_log("$key, get result from database, is_allowed = 1, false"); return false; } $checkSeedBoxAdminSql = sprintf( - 'select id from seed_box_records where `ip_begin_numeric` <= "%s" and `ip_end_numeric` >= "%s" and `type` = %s and `version` = %s and `status` = %s and `is_allowed` = 0 limit 1', + 'select id from seed_box_records where `ip_begin_numeric` <= "%s" and `ip_end_numeric` >= "%s" and `type` = %s and `version` = %s and `status` = %s and `is_allowed` = 0 and asn = 0 limit 1', $ipNumeric, $ipNumeric, \App\Models\SeedBoxRecord::TYPE_ADMIN, $ipVersion, \App\Models\SeedBoxRecord::STATUS_ALLOWED ); $res = \Nexus\Database\NexusDB::select($checkSeedBoxAdminSql); @@ -891,7 +924,7 @@ function isIPSeedBox($ip, $uid, $withoutCache = false): bool } if ($uid !== null) { $checkSeedBoxUserSql = sprintf( - 'select id from seed_box_records where `ip_begin_numeric` <= "%s" and `ip_end_numeric` >= "%s" and `uid` = %s and `type` = %s and `version` = %s and `status` = %s and `is_allowed` = 0 limit 1', + 'select id from seed_box_records where `ip_begin_numeric` <= "%s" and `ip_end_numeric` >= "%s" and `uid` = %s and `type` = %s and `version` = %s and `status` = %s and `is_allowed` = 0 and asn = 0 limit 1', $ipNumeric, $ipNumeric, $uid, \App\Models\SeedBoxRecord::TYPE_USER, $ipVersion, \App\Models\SeedBoxRecord::STATUS_ALLOWED ); $res = \Nexus\Database\NexusDB::select($checkSeedBoxUserSql); diff --git a/resources/lang/en/label.php b/resources/lang/en/label.php index 68c03dda..2276f213 100644 --- a/resources/lang/en/label.php +++ b/resources/lang/en/label.php @@ -259,10 +259,11 @@ return [ 'ip' => 'IP(Block)', 'ip_begin' => 'Begin IP', 'ip_end' => 'End IP', - 'ip_help' => 'Fill in the begin IP + end IP, or IP (Block), not both', + 'ip_help' => 'Fill in the ASN/begin IP + end IP/IP (Block), only one of the three', 'status' => 'Status', 'is_allowed' => 'Is whitelisted', 'is_allowed_help' => 'IPs in the whitelist are not affected by the SeedBox rule', + 'asn' => 'ASN', ], 'menu' => [ 'label' => 'Custom menu', diff --git a/resources/lang/zh_CN/label.php b/resources/lang/zh_CN/label.php index 256e6f3a..e7271cfd 100644 --- a/resources/lang/zh_CN/label.php +++ b/resources/lang/zh_CN/label.php @@ -265,10 +265,11 @@ return [ 'ip' => 'IP(段)', 'ip_begin' => '起始 IP', 'ip_end' => '结束 IP', - 'ip_help' => '填写起始 IP + 结束 IP,或 IP(段),不要同时填写', + 'ip_help' => '填写 ASN/起始 IP + 结束 IP/IP(段),不要同时填写', 'status' => '状态', 'is_allowed' => '是否白名单', 'is_allowed_help' => '位于白名单中的 IP 不受 SeedBox 规则影响', + 'asn' => 'ASN', ], 'menu' => [ 'label' => '自定义菜单', diff --git a/resources/lang/zh_TW/label.php b/resources/lang/zh_TW/label.php index 8149527d..6f6c95b9 100644 --- a/resources/lang/zh_TW/label.php +++ b/resources/lang/zh_TW/label.php @@ -259,10 +259,11 @@ return [ 'ip' => 'IP(段)', 'ip_begin' => '起始 IP', 'ip_end' => '結束 IP', - 'ip_help' => '填寫起始 IP + 結束 IP,或 IP(段),不要同時填寫', + 'ip_help' => '填寫ASN/起始 IP + 結束 IP/IP(段),不要同時填寫', 'status' => '狀態', 'is_allowed' => '是否白名單', 'is_allowed_help' => '位於白名單中的 IP 不受 SeedBox 規則影響', + 'asn' => 'ASN', ], 'menu' => [ 'label' => '自定義菜單',