mirror of
https://github.com/lkddi/nexusphp.git
synced 2026-04-03 14:10:57 +08:00
refactor isSeedBox judgement
This commit is contained in:
@@ -9,6 +9,7 @@ use App\Models\PersonalAccessToken;
|
||||
use App\Models\Torrent;
|
||||
use App\Models\User;
|
||||
use App\Repositories\ExamRepository;
|
||||
use App\Repositories\SeedBoxRepository;
|
||||
use App\Repositories\UploadRepository;
|
||||
use Illuminate\Console\Command;
|
||||
use NexusPlugin\Menu\Filament\MenuItemResource\Pages\ManageMenuItems;
|
||||
@@ -55,9 +56,8 @@ class Test extends Command
|
||||
*/
|
||||
public function handle()
|
||||
{
|
||||
$rep = new MenuRepository();
|
||||
$result = \Nexus\Plugin\Plugin::listEnabled();
|
||||
dd($result);
|
||||
$rep = new SeedBoxRepository();
|
||||
$rep->updateCacheCronjob();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -6,6 +6,7 @@ use App\Jobs\CheckCleanup;
|
||||
use App\Jobs\CheckQueueFailedJobs;
|
||||
use App\Jobs\MaintainPluginState;
|
||||
use App\Jobs\ManagePlugin;
|
||||
use App\Jobs\UpdateIsSeedBoxFromUserRecordsCache;
|
||||
use App\Utils\ThirdPartyJob;
|
||||
use Carbon\Carbon;
|
||||
use Illuminate\Console\Scheduling\Event;
|
||||
@@ -48,6 +49,7 @@ class Kernel extends ConsoleKernel
|
||||
$schedule->job(new CheckQueueFailedJobs())->everySixHours()->withoutOverlapping();
|
||||
$schedule->job(new ThirdPartyJob())->everyMinute()->withoutOverlapping();
|
||||
$schedule->job(new MaintainPluginState())->everyMinute()->withoutOverlapping();
|
||||
$schedule->job(new UpdateIsSeedBoxFromUserRecordsCache())->everySixHours()->withoutOverlapping();
|
||||
|
||||
$this->registerScheduleCleanup($schedule);
|
||||
}
|
||||
|
||||
10
app/Enums/SeedBoxRecord/IpAsnEnum.php
Normal file
10
app/Enums/SeedBoxRecord/IpAsnEnum.php
Normal file
@@ -0,0 +1,10 @@
|
||||
<?php
|
||||
|
||||
namespace App\Enums\SeedBoxRecord;
|
||||
|
||||
enum IpAsnEnum: string {
|
||||
|
||||
case IP = "IP";
|
||||
|
||||
case ASN = "ASN";
|
||||
}
|
||||
9
app/Enums/SeedBoxRecord/IsAllowedEnum.php
Normal file
9
app/Enums/SeedBoxRecord/IsAllowedEnum.php
Normal file
@@ -0,0 +1,9 @@
|
||||
<?php
|
||||
|
||||
namespace App\Enums\SeedBoxRecord;
|
||||
|
||||
enum IsAllowedEnum: int {
|
||||
case YES = 1;
|
||||
|
||||
case NO = 0;
|
||||
}
|
||||
10
app/Enums/SeedBoxRecord/TypeEnum.php
Normal file
10
app/Enums/SeedBoxRecord/TypeEnum.php
Normal file
@@ -0,0 +1,10 @@
|
||||
<?php
|
||||
|
||||
namespace App\Enums\SeedBoxRecord;
|
||||
|
||||
enum TypeEnum: int {
|
||||
|
||||
case USER = 1;
|
||||
|
||||
case ADMIN = 2;
|
||||
}
|
||||
@@ -48,8 +48,8 @@ class SeedBoxRecordResource extends Resource
|
||||
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_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')),
|
||||
Forms\Components\Toggle::make('is_allowed')
|
||||
->label(__('label.seed_box_record.is_allowed'))
|
||||
|
||||
@@ -2,16 +2,19 @@
|
||||
|
||||
namespace App\Filament\Resources\System\SeedBoxRecordResource\Pages;
|
||||
|
||||
use App\Exceptions\SeedBoxYesException;
|
||||
use App\Filament\PageList;
|
||||
use App\Filament\Resources\System\SeedBoxRecordResource;
|
||||
use Filament\Pages\Actions;
|
||||
use App\Repositories\SeedBoxRepository;
|
||||
use Filament\Actions;
|
||||
use Filament\Forms;
|
||||
use Illuminate\Support\HtmlString;
|
||||
|
||||
class ListSeedBoxRecords extends PageList
|
||||
{
|
||||
protected static string $resource = SeedBoxRecordResource::class;
|
||||
|
||||
protected static ?array $checkResult = null;
|
||||
|
||||
protected function getHeaderActions(): array
|
||||
{
|
||||
return [
|
||||
@@ -23,17 +26,40 @@ class ListSeedBoxRecords extends PageList
|
||||
Forms\Components\TextInput::make('uid')->required()->label('UID'),
|
||||
])
|
||||
->modalHeading(__('admin.resources.seed_box_record.check_modal_header'))
|
||||
->action(function ($data) {
|
||||
try {
|
||||
isIPSeedBox($data['ip'], $data['uid'], true, true);
|
||||
send_admin_success_notification(nexus_trans("seed-box.is_seed_box_no"));
|
||||
} catch (SeedBoxYesException $exception) {
|
||||
send_admin_fail_notification(nexus_trans("seed-box.is_seed_box_yes", ['id' => $exception->getId()]));
|
||||
} catch (\Throwable $throwable) {
|
||||
do_log($throwable->getMessage() . $throwable->getTraceAsString(), "error");
|
||||
send_admin_fail_notification($throwable->getMessage());
|
||||
}
|
||||
->action(function (array $data) {
|
||||
$result = SeedBoxRepository::isSeedBoxFromUserRecords($data['uid'], $data['ip']);
|
||||
self::$checkResult = $result;
|
||||
// return $result;
|
||||
// $this->replaceMountedAction("checkResult", ['result' => $result]);
|
||||
// if ($checkResult['result']) {
|
||||
// send_admin_success_notification(nexus_trans("seed-box.is_seed_box_yes", ['desc' => $checkResult['desc']]));
|
||||
// } else {
|
||||
// send_admin_fail_notification(nexus_trans("seed-box.is_seed_box_no", ['desc' => $checkResult['desc']]));
|
||||
// }
|
||||
})
|
||||
->registerModalActions([
|
||||
Actions\Action::make('checkResult')
|
||||
->modalHeading(function () {
|
||||
if (self::$checkResult !== null) {
|
||||
if (self::$checkResult['result']) {
|
||||
return nexus_trans("seed-box.is_seed_box_yes");
|
||||
} else {
|
||||
return nexus_trans("seed-box.is_seed_box_no");
|
||||
}
|
||||
}
|
||||
return 'Unknown';
|
||||
})
|
||||
->action(null)
|
||||
->modalSubmitAction(false)
|
||||
->modalCancelAction(false)
|
||||
->modalDescription(fn () => new HtmlString(self::$checkResult['desc'] ?? ''))
|
||||
// ->modalContent(fn () => new HtmlString(self::$checkResult['desc'] ?? ''))
|
||||
])
|
||||
->after(function() {
|
||||
$this->mountAction("checkResult");
|
||||
})
|
||||
,
|
||||
];
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
39
app/Jobs/UpdateIsSeedBoxFromUserRecordsCache.php
Normal file
39
app/Jobs/UpdateIsSeedBoxFromUserRecordsCache.php
Normal file
@@ -0,0 +1,39 @@
|
||||
<?php
|
||||
|
||||
namespace App\Jobs;
|
||||
|
||||
use App\Enums\SeedBoxRecord\IpAsnEnum;
|
||||
use App\Enums\SeedBoxRecord\IsAllowedEnum;
|
||||
use App\Repositories\SeedBoxRepository;
|
||||
use Illuminate\Contracts\Queue\ShouldQueue;
|
||||
use Illuminate\Foundation\Queue\Queueable;
|
||||
|
||||
class UpdateIsSeedBoxFromUserRecordsCache implements ShouldQueue
|
||||
{
|
||||
use Queueable;
|
||||
|
||||
/**
|
||||
* Create a new job instance.
|
||||
*/
|
||||
public function __construct()
|
||||
{
|
||||
//
|
||||
}
|
||||
|
||||
/**
|
||||
* Execute the job.
|
||||
*/
|
||||
public function handle(): void
|
||||
{
|
||||
$rep = new SeedBoxRepository();
|
||||
foreach (IpAsnEnum::cases() as $field) {
|
||||
foreach (IsAllowedEnum::cases() as $isAllowed) {
|
||||
$rep->updateUserCacheCronjob($isAllowed, $field);
|
||||
do_log("SeedBoxRepository::updateUserCacheCronjob isAllowed: $isAllowed->name, field: $field->name success");
|
||||
$rep->updateAdminCacheCronjob($isAllowed, $field);
|
||||
do_log("SeedBoxRepository::updateAdminCacheCronjob isAllowed: $isAllowed->name, field: $field->name success");
|
||||
}
|
||||
}
|
||||
do_log("UpdateIsSeedBoxFromUserRecordsCache done!");
|
||||
}
|
||||
}
|
||||
@@ -2,7 +2,12 @@
|
||||
|
||||
namespace App\Models;
|
||||
|
||||
use App\Enums\SeedBoxRecord\IpAsnEnum;
|
||||
use App\Enums\SeedBoxRecord\IsAllowedEnum;
|
||||
use App\Enums\SeedBoxRecord\TypeEnum;
|
||||
use App\Repositories\SeedBoxRepository;
|
||||
use Illuminate\Database\Eloquent\Casts\Attribute;
|
||||
use Nexus\Database\NexusDB;
|
||||
|
||||
class SeedBoxRecord extends NexusModel
|
||||
{
|
||||
@@ -30,6 +35,43 @@ class SeedBoxRecord extends NexusModel
|
||||
self::STATUS_DENIED => ['text' => 'Denied'],
|
||||
];
|
||||
|
||||
protected static function booted(): void
|
||||
{
|
||||
static::saved(function (SeedBoxRecord $model) {
|
||||
self::updateCache($model);
|
||||
});
|
||||
static::deleted(function (SeedBoxRecord $model) {
|
||||
self::updateCache($model);
|
||||
});
|
||||
}
|
||||
|
||||
private static function updateCache(SeedBoxRecord $model): void
|
||||
{
|
||||
SeedBoxRepository::updateCache(
|
||||
$model->type == TypeEnum::ADMIN->value ? 0 : $model->uid,
|
||||
TypeEnum::from($model->type),
|
||||
IsAllowedEnum::from($model->is_allowed),
|
||||
!empty($model->ip) ? IpAsnEnum::IP : IpAsnEnum::ASN,
|
||||
);
|
||||
}
|
||||
|
||||
public static function getValidQuery(TypeEnum $type, IsAllowedEnum $isAllowed, IpAsnEnum $field)
|
||||
{
|
||||
$query = self::query()
|
||||
->where('status', self::STATUS_ALLOWED)
|
||||
->where('type', $type->value)
|
||||
->where('is_allowed', $isAllowed->value)
|
||||
;
|
||||
if ($field == IpAsnEnum::IP) {
|
||||
$query->whereNotNull("ip");
|
||||
} elseif ($field == IpAsnEnum::ASN) {
|
||||
$query->where("asn", ">", 0);
|
||||
} else {
|
||||
throw new \InvalidArgumentException("Invalid ipOrAsn");
|
||||
}
|
||||
return $query;
|
||||
}
|
||||
|
||||
protected function typeText(): Attribute
|
||||
{
|
||||
return new Attribute(
|
||||
|
||||
@@ -80,7 +80,7 @@ class AppPanelProvider extends PanelProvider
|
||||
])
|
||||
->navigationItems([
|
||||
NavigationItem::make('Horizon')
|
||||
->label(nexus_trans('admin.sidebar.queue_monitor', [], Auth::user() ? get_langfolder_cookie(true) : 'en'))
|
||||
->label(fn () => nexus_trans('admin.sidebar.queue_monitor', [], Auth::user() ? get_langfolder_cookie(true) : 'en'))
|
||||
->icon('heroicon-o-presentation-chart-line')
|
||||
->group('System')
|
||||
->sort(99)
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
|
||||
|
||||
}
|
||||
|
||||
@@ -681,6 +681,9 @@ class UserRepository extends BaseRepository
|
||||
'login_logs' => 'uid',
|
||||
'oauth_access_tokens' => 'user_id',
|
||||
'oauth_auth_codes' => 'user_id',
|
||||
'seed_box_records' => 'uid',
|
||||
'user_modify_logs' => 'user_id',
|
||||
'messages' => 'receiver',
|
||||
];
|
||||
foreach ($tables as $table => $key) {
|
||||
NexusDB::statement(sprintf("delete from `%s` where `%s` in (%s)", $table, $key, $uidStr));
|
||||
|
||||
@@ -100,9 +100,9 @@ return [
|
||||
*/
|
||||
|
||||
'trim' => [
|
||||
'recent' => 60,
|
||||
'pending' => 60,
|
||||
'completed' => 60,
|
||||
'recent' => 60 * 72,
|
||||
'pending' => 60 * 72,
|
||||
'completed' => 60 * 72,
|
||||
'recent_failed' => 10080,
|
||||
'failed' => 10080,
|
||||
'monitored' => 10080,
|
||||
|
||||
@@ -0,0 +1,28 @@
|
||||
<?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('seed_box_records', function (Blueprint $table) {
|
||||
$table->index('uid');
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Reverse the migrations.
|
||||
*/
|
||||
public function down(): void
|
||||
{
|
||||
Schema::table('seed_box_records', function (Blueprint $table) {
|
||||
//
|
||||
});
|
||||
}
|
||||
};
|
||||
@@ -78,9 +78,8 @@ function peasant_to_user($down_floor_gb, $down_roof_gb, $minratio){
|
||||
while ($arr = mysql_fetch_assoc($res))
|
||||
{
|
||||
$locale = get_user_locale($arr['id']);
|
||||
$subject = nexus_trans("cleanup.msg_low_ratio_warning_removed", [], $locale);
|
||||
$msg = nexus_trans("cleanup.msg_your_ratio_warning_removed", [], $locale);
|
||||
|
||||
$subject = sqlesc(nexus_trans("cleanup.msg_low_ratio_warning_removed", [], $locale));
|
||||
$msg = sqlesc(nexus_trans("cleanup.msg_your_ratio_warning_removed", [], $locale));
|
||||
writecomment($arr['id'],"Leech Warning removed by System.");
|
||||
sql_query("UPDATE users SET class = 1, leechwarn = 'no', leechwarnuntil = null WHERE id = {$arr['id']}") or sqlerr(__FILE__, __LINE__);
|
||||
sql_query("INSERT INTO messages (sender, receiver, added, subject, msg) VALUES(0, {$arr['id']}, $dt, $subject, $msg)") or sqlerr(__FILE__, __LINE__);
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
<?php
|
||||
defined('VERSION_NUMBER') || define('VERSION_NUMBER', '1.9.0');
|
||||
defined('RELEASE_DATE') || define('RELEASE_DATE', '2025-05-08');
|
||||
defined('RELEASE_DATE') || define('RELEASE_DATE', '2025-05-11');
|
||||
defined('IN_TRACKER') || define('IN_TRACKER', false);
|
||||
defined('PROJECTNAME') || define("PROJECTNAME","NexusPHP");
|
||||
defined('NEXUSPHPURL') || define("NEXUSPHPURL","https://nexusphp.org");
|
||||
|
||||
@@ -2812,7 +2812,7 @@ if ($msgalert)
|
||||
|
||||
//seed box approval
|
||||
if (get_user_class() >= \App\Models\User::CLASS_ADMINISTRATOR && get_setting('seed_box.enabled') == 'yes') {
|
||||
$cacheKey = 'SEED_BOX_RECORD_APPROVAL_NONE';
|
||||
$cacheKey = \App\Repositories\SeedBoxRepository::APPROVAL_COUNT_CACHE_KEY;
|
||||
$toApprovalCounts = $Cache->get_value($cacheKey);
|
||||
if ($toApprovalCounts === false) {
|
||||
$toApprovalCounts = get_row_count('seed_box_records', 'where status = 0');
|
||||
|
||||
@@ -915,6 +915,15 @@ function isIPSeedBoxFromASN($ip, $exceptionWhenYes = false): bool
|
||||
return $result;
|
||||
}
|
||||
|
||||
/**
|
||||
* @deprecated
|
||||
* @param $ip
|
||||
* @param $uid
|
||||
* @param $withoutCache
|
||||
* @param $exceptionWhenYes
|
||||
* @return bool
|
||||
* @throws \App\Exceptions\SeedBoxYesException
|
||||
*/
|
||||
function isIPSeedBox($ip, $uid, $withoutCache = false, $exceptionWhenYes = false): bool
|
||||
{
|
||||
$key = "nexus_is_ip_seed_box:ip:$ip:uid:$uid";
|
||||
|
||||
@@ -337,10 +337,10 @@ $isSeedBoxRuleEnabled = get_setting('seed_box.enabled') == 'yes';
|
||||
$isIPSeedBox = false;
|
||||
if ($isSeedBoxRuleEnabled) {
|
||||
if (!empty($ipv4)) {
|
||||
$isIPSeedBox = isIPSeedBox($ipv4, $userid);
|
||||
$isIPSeedBox = \App\Repositories\SeedBoxRepository::isSeedBoxFromUserRecords($userid, $ipv4)['result'];
|
||||
}
|
||||
if (!$isIPSeedBox && !empty($ipv6)) {
|
||||
$isIPSeedBox = isIPSeedBox($ipv6, $userid);
|
||||
$isIPSeedBox = \App\Repositories\SeedBoxRepository::isSeedBoxFromUserRecords($userid, $ipv6)['result'];
|
||||
}
|
||||
}
|
||||
$log .= ", [SEED_BOX], isSeedBoxRuleEnabled: $isSeedBoxRuleEnabled, isIPSeedBox: $isIPSeedBox";
|
||||
|
||||
@@ -1007,8 +1007,8 @@ if (get_setting('seed_box.enabled') == 'yes') {
|
||||
$seedBox = '';
|
||||
$columnOperator = nexus_trans('label.seed_box_record.operator');
|
||||
$columnBandwidth = nexus_trans('label.seed_box_record.bandwidth');
|
||||
$columnIPBegin = nexus_trans('label.seed_box_record.ip_begin');
|
||||
$columnIPEnd = nexus_trans('label.seed_box_record.ip_end');
|
||||
// $columnIPBegin = nexus_trans('label.seed_box_record.ip_begin');
|
||||
// $columnIPEnd = nexus_trans('label.seed_box_record.ip_end');
|
||||
$columnIP = nexus_trans('label.seed_box_record.ip');
|
||||
$columnIPHelp = nexus_trans('label.seed_box_record.ip_help');
|
||||
$columnComment = nexus_trans('label.comment');
|
||||
@@ -1068,14 +1068,6 @@ CSS;
|
||||
<div class="label">{$columnBandwidth}</div>
|
||||
<div class="field"><input type="number" name="params[bandwidth]"></div>
|
||||
</div>
|
||||
<div class="form-control-row">
|
||||
<div class="label">{$columnIPBegin}</div>
|
||||
<div class="field"><input type="text" name="params[ip_begin]"></div>
|
||||
</div>
|
||||
<div class="form-control-row">
|
||||
<div class="label">{$columnIPEnd}</div>
|
||||
<div class="field"><input type="text" name="params[ip_end]"></div>
|
||||
</div>
|
||||
<div class="form-control-row">
|
||||
<div class="label">{$columnIP}</div>
|
||||
<div class="field"><input type="text" name="params[ip]"><div><small>{$columnIPHelp}</small></div></div>
|
||||
@@ -1097,8 +1089,9 @@ jQuery('#add-seed-box-btn').on('click', function () {
|
||||
btnAlign: 'c',
|
||||
yes: function () {
|
||||
let params = jQuery('#seed-box-form').serialize()
|
||||
jQuery('body').loading({stoppable: false});
|
||||
jQuery.post('ajax.php', params + "&action=addSeedBoxRecord", function (response) {
|
||||
console.log(response)
|
||||
jQuery('body').loading('stop');
|
||||
if (response.ret != 0) {
|
||||
layer.alert(response.msg)
|
||||
return
|
||||
@@ -1111,8 +1104,9 @@ jQuery('#add-seed-box-btn').on('click', function () {
|
||||
jQuery('#seed-box-table').on('click', '.remove-seed-box-btn', function () {
|
||||
let params = {action: "removeSeedBoxRecord", params: {id: jQuery(this).attr("data-id")}}
|
||||
layer.confirm("{$lang_functions['std_confirm_remove']}", window.nexusLayerOptions.confirm, function (index) {
|
||||
jQuery('body').loading({stoppable: false});
|
||||
jQuery.post('ajax.php', params, function (response) {
|
||||
console.log(response)
|
||||
jQuery('body').loading('stop');
|
||||
if (response.ret != 0) {
|
||||
layer.alert(response.msg, window.nexusLayerOptions.alert)
|
||||
return
|
||||
|
||||
446
public/vendor/livewire/livewire.esm.js
vendored
446
public/vendor/livewire/livewire.esm.js
vendored
File diff suppressed because it is too large
Load Diff
6
public/vendor/livewire/livewire.esm.js.map
vendored
6
public/vendor/livewire/livewire.esm.js.map
vendored
File diff suppressed because one or more lines are too long
312
public/vendor/livewire/livewire.js
vendored
312
public/vendor/livewire/livewire.js
vendored
@@ -370,9 +370,7 @@
|
||||
if (key === "")
|
||||
return object;
|
||||
return key.split(".").reduce((carry, i) => {
|
||||
if (carry === void 0)
|
||||
return void 0;
|
||||
return carry[i];
|
||||
return carry?.[i];
|
||||
}, object);
|
||||
}
|
||||
function dataSet(object, key, value) {
|
||||
@@ -499,6 +497,9 @@
|
||||
if (value === null || value === "") {
|
||||
el.value = "";
|
||||
}
|
||||
if (el.multiple && Array.isArray(value) && value.length === 0) {
|
||||
el.value = "";
|
||||
}
|
||||
});
|
||||
let clearFileInputValue = () => {
|
||||
el.value = null;
|
||||
@@ -901,7 +902,7 @@
|
||||
deferredMutations = deferredMutations.concat(mutations);
|
||||
return;
|
||||
}
|
||||
let addedNodes = /* @__PURE__ */ new Set();
|
||||
let addedNodes = [];
|
||||
let removedNodes = /* @__PURE__ */ new Set();
|
||||
let addedAttributes = /* @__PURE__ */ new Map();
|
||||
let removedAttributes = /* @__PURE__ */ new Map();
|
||||
@@ -909,8 +910,24 @@
|
||||
if (mutations[i].target._x_ignoreMutationObserver)
|
||||
continue;
|
||||
if (mutations[i].type === "childList") {
|
||||
mutations[i].addedNodes.forEach((node) => node.nodeType === 1 && addedNodes.add(node));
|
||||
mutations[i].removedNodes.forEach((node) => node.nodeType === 1 && removedNodes.add(node));
|
||||
mutations[i].removedNodes.forEach((node) => {
|
||||
if (node.nodeType !== 1)
|
||||
return;
|
||||
if (!node._x_marker)
|
||||
return;
|
||||
removedNodes.add(node);
|
||||
});
|
||||
mutations[i].addedNodes.forEach((node) => {
|
||||
if (node.nodeType !== 1)
|
||||
return;
|
||||
if (removedNodes.has(node)) {
|
||||
removedNodes.delete(node);
|
||||
return;
|
||||
}
|
||||
if (node._x_marker)
|
||||
return;
|
||||
addedNodes.push(node);
|
||||
});
|
||||
}
|
||||
if (mutations[i].type === "attributes") {
|
||||
let el = mutations[i].target;
|
||||
@@ -943,29 +960,15 @@
|
||||
onAttributeAddeds.forEach((i) => i(el, attrs));
|
||||
});
|
||||
for (let node of removedNodes) {
|
||||
if (addedNodes.has(node))
|
||||
if (addedNodes.some((i) => i.contains(node)))
|
||||
continue;
|
||||
onElRemoveds.forEach((i) => i(node));
|
||||
}
|
||||
addedNodes.forEach((node) => {
|
||||
node._x_ignoreSelf = true;
|
||||
node._x_ignore = true;
|
||||
});
|
||||
for (let node of addedNodes) {
|
||||
if (removedNodes.has(node))
|
||||
continue;
|
||||
if (!node.isConnected)
|
||||
continue;
|
||||
delete node._x_ignoreSelf;
|
||||
delete node._x_ignore;
|
||||
onElAddeds.forEach((i) => i(node));
|
||||
node._x_ignore = true;
|
||||
node._x_ignoreSelf = true;
|
||||
}
|
||||
addedNodes.forEach((node) => {
|
||||
delete node._x_ignoreSelf;
|
||||
delete node._x_ignore;
|
||||
});
|
||||
addedNodes = null;
|
||||
removedNodes = null;
|
||||
addedAttributes = null;
|
||||
@@ -1468,13 +1471,20 @@ ${expression ? 'Expression: "' + expression + '"\n\n' : ""}`, el);
|
||||
function interceptInit(callback) {
|
||||
initInterceptors2.push(callback);
|
||||
}
|
||||
var markerDispenser = 1;
|
||||
function initTree(el, walker = walk, intercept = () => {
|
||||
}) {
|
||||
if (findClosest(el, (i) => i._x_ignore))
|
||||
return;
|
||||
deferHandlingDirectives(() => {
|
||||
walker(el, (el2, skip) => {
|
||||
if (el2._x_marker)
|
||||
return;
|
||||
intercept(el2, skip);
|
||||
initInterceptors2.forEach((i) => i(el2, skip));
|
||||
directives(el2, el2.attributes).forEach((handle) => handle());
|
||||
if (!el2._x_ignore)
|
||||
el2._x_marker = markerDispenser++;
|
||||
el2._x_ignore && skip();
|
||||
});
|
||||
});
|
||||
@@ -1483,6 +1493,7 @@ ${expression ? 'Expression: "' + expression + '"\n\n' : ""}`, el);
|
||||
walker(root, (el) => {
|
||||
cleanupElement(el);
|
||||
cleanupAttributes(el);
|
||||
delete el._x_marker;
|
||||
});
|
||||
}
|
||||
function warnAboutMissingPlugins() {
|
||||
@@ -2284,7 +2295,7 @@ ${expression ? 'Expression: "' + expression + '"\n\n' : ""}`, el);
|
||||
get raw() {
|
||||
return raw;
|
||||
},
|
||||
version: "3.14.3",
|
||||
version: "3.14.9",
|
||||
flushAndStopDeferringMutations,
|
||||
dontAutoEvaluateFunctions,
|
||||
disableEffectScheduling,
|
||||
@@ -3131,7 +3142,6 @@ ${expression ? 'Expression: "' + expression + '"\n\n' : ""}`, el);
|
||||
placeInDom(clone2, target, modifiers);
|
||||
skipDuringClone(() => {
|
||||
initTree(clone2);
|
||||
clone2._x_ignore = true;
|
||||
})();
|
||||
});
|
||||
el._x_teleportPutBack = () => {
|
||||
@@ -4353,9 +4363,11 @@ ${expression ? 'Expression: "' + expression + '"\n\n' : ""}`, el);
|
||||
"on": "$on",
|
||||
"el": "$el",
|
||||
"id": "$id",
|
||||
"js": "$js",
|
||||
"get": "$get",
|
||||
"set": "$set",
|
||||
"call": "$call",
|
||||
"hook": "$hook",
|
||||
"commit": "$commit",
|
||||
"watch": "$watch",
|
||||
"entangle": "$entangle",
|
||||
@@ -4423,6 +4435,14 @@ ${expression ? 'Expression: "' + expression + '"\n\n' : ""}`, el);
|
||||
wireProperty("$id", (component) => {
|
||||
return component.id;
|
||||
});
|
||||
wireProperty("$js", (component) => {
|
||||
let fn = component.addJsAction.bind(component);
|
||||
let jsActions = component.getJsActions();
|
||||
Object.keys(jsActions).forEach((name) => {
|
||||
fn[name] = component.getJsAction(name);
|
||||
});
|
||||
return fn;
|
||||
});
|
||||
wireProperty("$set", (component) => async (property, value, live = true) => {
|
||||
dataSet(component.reactive, property, value);
|
||||
if (live) {
|
||||
@@ -4450,6 +4470,16 @@ ${expression ? 'Expression: "' + expression + '"\n\n' : ""}`, el);
|
||||
wireProperty("$refresh", (component) => component.$wire.$commit);
|
||||
wireProperty("$commit", (component) => async () => await requestCommit(component));
|
||||
wireProperty("$on", (component) => (...params) => listen2(component, ...params));
|
||||
wireProperty("$hook", (component) => (name, callback) => {
|
||||
let unhook = on2(name, ({ component: hookComponent, ...params }) => {
|
||||
if (hookComponent === void 0)
|
||||
return callback(params);
|
||||
if (hookComponent.id === component.id)
|
||||
return callback({ component: hookComponent, ...params });
|
||||
});
|
||||
component.addCleanup(unhook);
|
||||
return unhook;
|
||||
});
|
||||
wireProperty("$dispatch", (component) => (...params) => dispatch3(component, ...params));
|
||||
wireProperty("$dispatchSelf", (component) => (...params) => dispatchSelf(component, ...params));
|
||||
wireProperty("$dispatchTo", () => (...params) => dispatchTo(...params));
|
||||
@@ -4508,6 +4538,7 @@ ${expression ? 'Expression: "' + expression + '"\n\n' : ""}`, el);
|
||||
this.ephemeral = extractData(deepClone(this.snapshot.data));
|
||||
this.reactive = Alpine.reactive(this.ephemeral);
|
||||
this.queuedUpdates = {};
|
||||
this.jsActions = {};
|
||||
this.$wire = generateWireObject(this, this.reactive);
|
||||
this.cleanups = [];
|
||||
this.processEffects(this.effects);
|
||||
@@ -4583,6 +4614,18 @@ ${expression ? 'Expression: "' + expression + '"\n\n' : ""}`, el);
|
||||
}
|
||||
el.setAttribute("wire:effects", JSON.stringify(effects));
|
||||
}
|
||||
addJsAction(name, action) {
|
||||
this.jsActions[name] = action;
|
||||
}
|
||||
hasJsAction(name) {
|
||||
return this.jsActions[name] !== void 0;
|
||||
}
|
||||
getJsAction(name) {
|
||||
return this.jsActions[name].bind(this.$wire);
|
||||
}
|
||||
getJsActions() {
|
||||
return this.jsActions;
|
||||
}
|
||||
addCleanup(cleanup2) {
|
||||
this.cleanups.push(cleanup2);
|
||||
}
|
||||
@@ -4709,6 +4752,16 @@ ${expression ? 'Expression: "' + expression + '"\n\n' : ""}`, el);
|
||||
}
|
||||
});
|
||||
}
|
||||
function globalDirective(name, callback) {
|
||||
if (customDirectiveNames.has(name))
|
||||
return;
|
||||
customDirectiveNames.add(name);
|
||||
on2("directive.global.init", ({ el, directive: directive3, cleanup: cleanup2 }) => {
|
||||
if (directive3.value === name) {
|
||||
callback({ el, directive: directive3, cleanup: cleanup2 });
|
||||
}
|
||||
});
|
||||
}
|
||||
function getDirectives(el) {
|
||||
return new DirectiveManager(el);
|
||||
}
|
||||
@@ -4771,7 +4824,7 @@ ${expression ? 'Expression: "' + expression + '"\n\n' : ""}`, el);
|
||||
}
|
||||
};
|
||||
|
||||
// ../../../../usr/local/lib/node_modules/@alpinejs/collapse/dist/module.esm.js
|
||||
// ../alpine/packages/collapse/dist/module.esm.js
|
||||
function src_default2(Alpine3) {
|
||||
Alpine3.directive("collapse", collapse);
|
||||
collapse.inline = (el, { modifiers }) => {
|
||||
@@ -4821,7 +4874,7 @@ ${expression ? 'Expression: "' + expression + '"\n\n' : ""}`, el);
|
||||
start: { height: current + "px" },
|
||||
end: { height: full + "px" }
|
||||
}, () => el._x_isShown = true, () => {
|
||||
if (el.getBoundingClientRect().height == full) {
|
||||
if (Math.abs(el.getBoundingClientRect().height - full) < 1) {
|
||||
el.style.overflow = null;
|
||||
}
|
||||
});
|
||||
@@ -4865,7 +4918,7 @@ ${expression ? 'Expression: "' + expression + '"\n\n' : ""}`, el);
|
||||
}
|
||||
var module_default2 = src_default2;
|
||||
|
||||
// ../../../../usr/local/lib/node_modules/@alpinejs/focus/dist/module.esm.js
|
||||
// ../alpine/packages/focus/dist/module.esm.js
|
||||
var candidateSelectors = ["input", "select", "textarea", "a[href]", "button", "[tabindex]:not(slot)", "audio[controls]", "video[controls]", '[contenteditable]:not([contenteditable="false"])', "details>summary:first-of-type", "details"];
|
||||
var candidateSelector = /* @__PURE__ */ candidateSelectors.join(",");
|
||||
var NoElement = typeof Element === "undefined";
|
||||
@@ -5814,7 +5867,7 @@ ${expression ? 'Expression: "' + expression + '"\n\n' : ""}`, el);
|
||||
}
|
||||
var module_default3 = src_default3;
|
||||
|
||||
// ../../../../usr/local/lib/node_modules/@alpinejs/persist/dist/module.esm.js
|
||||
// ../alpine/packages/persist/dist/module.esm.js
|
||||
function src_default4(Alpine3) {
|
||||
let persist = () => {
|
||||
let alias;
|
||||
@@ -5876,7 +5929,7 @@ ${expression ? 'Expression: "' + expression + '"\n\n' : ""}`, el);
|
||||
}
|
||||
var module_default4 = src_default4;
|
||||
|
||||
// ../../../../usr/local/lib/node_modules/@alpinejs/intersect/dist/module.esm.js
|
||||
// ../alpine/packages/intersect/dist/module.esm.js
|
||||
function src_default5(Alpine3) {
|
||||
Alpine3.directive("intersect", Alpine3.skipDuringClone((el, { value, expression, modifiers }, { evaluateLater: evaluateLater2, cleanup: cleanup2 }) => {
|
||||
let evaluate3 = evaluateLater2(expression);
|
||||
@@ -7633,6 +7686,8 @@ ${expression ? 'Expression: "' + expression + '"\n\n' : ""}`, el);
|
||||
|
||||
// js/plugins/navigate/popover.js
|
||||
function packUpPersistedPopovers(persistedEl) {
|
||||
if (!isPopoverSupported())
|
||||
return;
|
||||
persistedEl.querySelectorAll(":popover-open").forEach((el) => {
|
||||
el.setAttribute("data-navigate-popover-open", "");
|
||||
let animations = el.getAnimations();
|
||||
@@ -7651,6 +7706,8 @@ ${expression ? 'Expression: "' + expression + '"\n\n' : ""}`, el);
|
||||
});
|
||||
}
|
||||
function unPackPersistedPopovers(persistedEl) {
|
||||
if (!isPopoverSupported())
|
||||
return;
|
||||
persistedEl.querySelectorAll("[data-navigate-popover-open]").forEach((el) => {
|
||||
el.removeAttribute("data-navigate-popover-open");
|
||||
queueMicrotask(() => {
|
||||
@@ -7668,6 +7725,9 @@ ${expression ? 'Expression: "' + expression + '"\n\n' : ""}`, el);
|
||||
});
|
||||
});
|
||||
}
|
||||
function isPopoverSupported() {
|
||||
return typeof document.createElement("div").showPopover === "function";
|
||||
}
|
||||
|
||||
// js/plugins/navigate/page.js
|
||||
var oldBodyScriptTagHashes = [];
|
||||
@@ -7677,6 +7737,7 @@ ${expression ? 'Expression: "' + expression + '"\n\n' : ""}`, el);
|
||||
];
|
||||
function swapCurrentPageWithNewHtml(html, andThen) {
|
||||
let newDocument = new DOMParser().parseFromString(html, "text/html");
|
||||
let newHtml = newDocument.documentElement;
|
||||
let newBody = document.adoptNode(newDocument.body);
|
||||
let newHead = document.adoptNode(newDocument.head);
|
||||
oldBodyScriptTagHashes = oldBodyScriptTagHashes.concat(Array.from(document.body.querySelectorAll("script")).map((i) => {
|
||||
@@ -7684,6 +7745,7 @@ ${expression ? 'Expression: "' + expression + '"\n\n' : ""}`, el);
|
||||
}));
|
||||
let afterRemoteScriptsHaveLoaded = () => {
|
||||
};
|
||||
replaceHtmlAttributes(newHtml);
|
||||
mergeNewHead(newHead).finally(() => {
|
||||
afterRemoteScriptsHaveLoaded();
|
||||
});
|
||||
@@ -7703,6 +7765,21 @@ ${expression ? 'Expression: "' + expression + '"\n\n' : ""}`, el);
|
||||
i.replaceWith(cloneScriptTag(i));
|
||||
});
|
||||
}
|
||||
function replaceHtmlAttributes(newHtmlElement) {
|
||||
let currentHtmlElement = document.documentElement;
|
||||
Array.from(newHtmlElement.attributes).forEach((attr) => {
|
||||
const name = attr.name;
|
||||
const value = attr.value;
|
||||
if (currentHtmlElement.getAttribute(name) !== value) {
|
||||
currentHtmlElement.setAttribute(name, value);
|
||||
}
|
||||
});
|
||||
Array.from(currentHtmlElement.attributes).forEach((attr) => {
|
||||
if (!newHtmlElement.hasAttribute(attr.name)) {
|
||||
currentHtmlElement.removeAttribute(attr.name);
|
||||
}
|
||||
});
|
||||
}
|
||||
function mergeNewHead(newHead) {
|
||||
let children = Array.from(document.head.children);
|
||||
let headChildrenHtmlLookup = children.map((i) => i.outerHTML);
|
||||
@@ -7736,6 +7813,8 @@ ${expression ? 'Expression: "' + expression + '"\n\n' : ""}`, el);
|
||||
child.remove();
|
||||
}
|
||||
for (let child of Array.from(newHead.children)) {
|
||||
if (child.tagName.toLowerCase() === "noscript")
|
||||
continue;
|
||||
document.head.appendChild(child);
|
||||
}
|
||||
return Promise.all(remoteScriptsPromises);
|
||||
@@ -8083,14 +8162,22 @@ ${expression ? 'Expression: "' + expression + '"\n\n' : ""}`, el);
|
||||
if (!state.alpine)
|
||||
state.alpine = {};
|
||||
state.alpine[key] = unwrap(object);
|
||||
window.history.replaceState(state, "", url.toString());
|
||||
try {
|
||||
window.history.replaceState(state, "", url.toString());
|
||||
} catch (e) {
|
||||
console.error(e);
|
||||
}
|
||||
}
|
||||
function push(url, key, object) {
|
||||
let state = window.history.state || {};
|
||||
if (!state.alpine)
|
||||
state.alpine = {};
|
||||
state = { alpine: { ...state.alpine, ...{ [key]: unwrap(object) } } };
|
||||
window.history.pushState(state, "", url.toString());
|
||||
try {
|
||||
window.history.pushState(state, "", url.toString());
|
||||
} catch (e) {
|
||||
console.error(e);
|
||||
}
|
||||
}
|
||||
function unwrap(object) {
|
||||
if (object === void 0)
|
||||
@@ -8103,24 +8190,24 @@ ${expression ? 'Expression: "' + expression + '"\n\n' : ""}`, el);
|
||||
let search = url.search;
|
||||
if (!search)
|
||||
return false;
|
||||
let data2 = fromQueryString(search);
|
||||
let data2 = fromQueryString(search, key);
|
||||
return Object.keys(data2).includes(key);
|
||||
},
|
||||
get(url, key) {
|
||||
let search = url.search;
|
||||
if (!search)
|
||||
return false;
|
||||
let data2 = fromQueryString(search);
|
||||
let data2 = fromQueryString(search, key);
|
||||
return data2[key];
|
||||
},
|
||||
set(url, key, value) {
|
||||
let data2 = fromQueryString(url.search);
|
||||
let data2 = fromQueryString(url.search, key);
|
||||
data2[key] = stripNulls(unwrap(value));
|
||||
url.search = toQueryString(data2);
|
||||
return url;
|
||||
},
|
||||
remove(url, key) {
|
||||
let data2 = fromQueryString(url.search);
|
||||
let data2 = fromQueryString(url.search, key);
|
||||
delete data2[key];
|
||||
url.search = toQueryString(data2);
|
||||
return url;
|
||||
@@ -8156,7 +8243,7 @@ ${expression ? 'Expression: "' + expression + '"\n\n' : ""}`, el);
|
||||
let entries = buildQueryStringEntries(data2);
|
||||
return Object.entries(entries).map(([key, value]) => `${key}=${value}`).join("&");
|
||||
}
|
||||
function fromQueryString(search) {
|
||||
function fromQueryString(search, queryKey) {
|
||||
search = search.replace("?", "");
|
||||
if (search === "")
|
||||
return {};
|
||||
@@ -8175,10 +8262,12 @@ ${expression ? 'Expression: "' + expression + '"\n\n' : ""}`, el);
|
||||
if (typeof value == "undefined")
|
||||
return;
|
||||
value = decodeURIComponent(value.replaceAll("+", "%20"));
|
||||
if (!key.includes("[")) {
|
||||
let decodedKey = decodeURIComponent(key);
|
||||
let shouldBeHandledAsArray = decodedKey.includes("[") && decodedKey.startsWith(queryKey);
|
||||
if (!shouldBeHandledAsArray) {
|
||||
data2[key] = value;
|
||||
} else {
|
||||
let dotNotatedKey = key.replaceAll("[", ".").replaceAll("]", "");
|
||||
let dotNotatedKey = decodedKey.replaceAll("[", ".").replaceAll("]", "");
|
||||
insertDotNotatedValueIntoData(dotNotatedKey, value, data2);
|
||||
}
|
||||
});
|
||||
@@ -8209,7 +8298,8 @@ ${expression ? 'Expression: "' + expression + '"\n\n' : ""}`, el);
|
||||
return swapElements(from2, to);
|
||||
}
|
||||
let updateChildrenOnly = false;
|
||||
if (shouldSkip(updating, from2, to, () => updateChildrenOnly = true))
|
||||
let skipChildren = false;
|
||||
if (shouldSkipChildren(updating, () => skipChildren = true, from2, to, () => updateChildrenOnly = true))
|
||||
return;
|
||||
if (from2.nodeType === 1 && window.Alpine) {
|
||||
window.Alpine.cloneNode(from2, to);
|
||||
@@ -8226,7 +8316,9 @@ ${expression ? 'Expression: "' + expression + '"\n\n' : ""}`, el);
|
||||
patchAttributes(from2, to);
|
||||
}
|
||||
updated(from2, to);
|
||||
patchChildren(from2, to);
|
||||
if (!skipChildren) {
|
||||
patchChildren(from2, to);
|
||||
}
|
||||
}
|
||||
function differentElementNamesTypesOrKeys(from2, to) {
|
||||
return from2.nodeType != to.nodeType || from2.nodeName != to.nodeName || getKey(from2) != getKey(to);
|
||||
@@ -8286,6 +8378,7 @@ ${expression ? 'Expression: "' + expression + '"\n\n' : ""}`, el);
|
||||
let holdover = fromKeyHoldovers[toKey];
|
||||
from2.appendChild(holdover);
|
||||
currentFrom = holdover;
|
||||
fromKey = getKey(currentFrom);
|
||||
} else {
|
||||
if (!shouldSkip(adding, currentTo)) {
|
||||
let clone2 = currentTo.cloneNode(true);
|
||||
@@ -8359,6 +8452,7 @@ ${expression ? 'Expression: "' + expression + '"\n\n' : ""}`, el);
|
||||
if (fromKeys[toKey]) {
|
||||
currentFrom.replaceWith(fromKeys[toKey]);
|
||||
currentFrom = fromKeys[toKey];
|
||||
fromKey = getKey(currentFrom);
|
||||
}
|
||||
}
|
||||
if (toKey && fromKey) {
|
||||
@@ -8367,6 +8461,7 @@ ${expression ? 'Expression: "' + expression + '"\n\n' : ""}`, el);
|
||||
fromKeyHoldovers[fromKey] = currentFrom;
|
||||
currentFrom.replaceWith(fromKeyNode);
|
||||
currentFrom = fromKeyNode;
|
||||
fromKey = getKey(currentFrom);
|
||||
} else {
|
||||
fromKeyHoldovers[fromKey] = currentFrom;
|
||||
currentFrom = addNodeBefore(from2, currentTo, currentFrom);
|
||||
@@ -8437,6 +8532,11 @@ ${expression ? 'Expression: "' + expression + '"\n\n' : ""}`, el);
|
||||
hook(...args, () => skip = true);
|
||||
return skip;
|
||||
}
|
||||
function shouldSkipChildren(hook, skipChildren, ...args) {
|
||||
let skip = false;
|
||||
hook(...args, () => skip = true, skipChildren);
|
||||
return skip;
|
||||
}
|
||||
var patched = false;
|
||||
function createElement(html) {
|
||||
const template = document.createElement("template");
|
||||
@@ -8512,6 +8612,8 @@ ${expression ? 'Expression: "' + expression + '"\n\n' : ""}`, el);
|
||||
let fromId = from && from._x_bindings && from._x_bindings.id;
|
||||
if (!fromId)
|
||||
return;
|
||||
if (!to.setAttribute)
|
||||
return;
|
||||
to.setAttribute("id", fromId);
|
||||
to.id = fromId;
|
||||
}
|
||||
@@ -8520,7 +8622,7 @@ ${expression ? 'Expression: "' + expression + '"\n\n' : ""}`, el);
|
||||
}
|
||||
var module_default8 = src_default8;
|
||||
|
||||
// ../../../../usr/local/lib/node_modules/@alpinejs/mask/dist/module.esm.js
|
||||
// ../alpine/packages/mask/dist/module.esm.js
|
||||
function src_default9(Alpine3) {
|
||||
Alpine3.directive("mask", (el, { value, expression }, { effect: effect3, evaluateLater: evaluateLater2, cleanup: cleanup2 }) => {
|
||||
let templateFn = () => expression;
|
||||
@@ -8546,8 +8648,13 @@ ${expression ? 'Expression: "' + expression + '"\n\n' : ""}`, el);
|
||||
} else {
|
||||
processInputValue(el, false);
|
||||
}
|
||||
if (el._x_model)
|
||||
if (el._x_model) {
|
||||
if (el._x_model.get() === el.value)
|
||||
return;
|
||||
if (el._x_model.get() === null && el.value === "")
|
||||
return;
|
||||
el._x_model.set(el.value);
|
||||
}
|
||||
});
|
||||
const controller = new AbortController();
|
||||
cleanup2(() => {
|
||||
@@ -8725,10 +8832,15 @@ ${expression ? 'Expression: "' + expression + '"\n\n' : ""}`, el);
|
||||
destroyComponent(component2.id);
|
||||
});
|
||||
}
|
||||
let directives2 = Array.from(el.getAttributeNames()).filter((name) => matchesForLivewireDirective(name)).map((name) => extractDirective(el, name));
|
||||
directives2.forEach((directive3) => {
|
||||
trigger2("directive.global.init", { el, directive: directive3, cleanup: (callback) => {
|
||||
module_default.onAttributeRemoved(el, directive3.raw, callback);
|
||||
} });
|
||||
});
|
||||
let component = closestComponent(el, false);
|
||||
if (component) {
|
||||
trigger2("element.init", { el, component });
|
||||
let directives2 = Array.from(el.getAttributeNames()).filter((name) => matchesForLivewireDirective(name)).map((name) => extractDirective(el, name));
|
||||
directives2.forEach((directive3) => {
|
||||
trigger2("directive.init", { el, component, directive: directive3, cleanup: (callback) => {
|
||||
module_default.onAttributeRemoved(el, directive3.raw, callback);
|
||||
@@ -8804,7 +8916,7 @@ ${expression ? 'Expression: "' + expression + '"\n\n' : ""}`, el);
|
||||
onlyIfScriptHasntBeenRunAlreadyForThisComponent(component, key, () => {
|
||||
let scriptContent = extractScriptTagContent(content);
|
||||
module_default.dontAutoEvaluateFunctions(() => {
|
||||
module_default.evaluate(component.el, scriptContent, { "$wire": component.$wire });
|
||||
module_default.evaluate(component.el, scriptContent, { "$wire": component.$wire, "$js": component.$wire.$js });
|
||||
});
|
||||
});
|
||||
});
|
||||
@@ -8876,6 +8988,10 @@ ${expression ? 'Expression: "' + expression + '"\n\n' : ""}`, el);
|
||||
}
|
||||
|
||||
// js/features/supportJsEvaluation.js
|
||||
module_default.magic("js", (el) => {
|
||||
let component = closestComponent(el);
|
||||
return component.$wire.js;
|
||||
});
|
||||
on2("effect", ({ component, effects }) => {
|
||||
let js = effects.js;
|
||||
let xjs = effects.xjs;
|
||||
@@ -8887,8 +9003,9 @@ ${expression ? 'Expression: "' + expression + '"\n\n' : ""}`, el);
|
||||
});
|
||||
}
|
||||
if (xjs) {
|
||||
xjs.forEach((expression) => {
|
||||
module_default.evaluate(component.el, expression);
|
||||
xjs.forEach(({ expression, params }) => {
|
||||
params = Object.values(params);
|
||||
module_default.evaluate(component.el, expression, { scope: component.jsActions, params });
|
||||
});
|
||||
}
|
||||
});
|
||||
@@ -8908,10 +9025,10 @@ ${expression ? 'Expression: "' + expression + '"\n\n' : ""}`, el);
|
||||
to.__livewire = component;
|
||||
trigger2("morph", { el, toEl: to, component });
|
||||
module_default.morph(el, to, {
|
||||
updating: (el2, toEl, childrenOnly, skip) => {
|
||||
updating: (el2, toEl, childrenOnly, skip, skipChildren) => {
|
||||
if (isntElement(el2))
|
||||
return;
|
||||
trigger2("morph.updating", { el: el2, toEl, component, skip, childrenOnly });
|
||||
trigger2("morph.updating", { el: el2, toEl, component, skip, childrenOnly, skipChildren });
|
||||
if (el2.__livewire_replace === true)
|
||||
el2.innerHTML = toEl.innerHTML;
|
||||
if (el2.__livewire_replace_self === true) {
|
||||
@@ -8922,6 +9039,8 @@ ${expression ? 'Expression: "' + expression + '"\n\n' : ""}`, el);
|
||||
return skip();
|
||||
if (el2.__livewire_ignore_self === true)
|
||||
childrenOnly();
|
||||
if (el2.__livewire_ignore_children === true)
|
||||
return skipChildren();
|
||||
if (isComponentRootEl(el2) && el2.getAttribute("wire:id") !== component.id)
|
||||
return skip();
|
||||
if (isComponentRootEl(el2))
|
||||
@@ -9354,6 +9473,14 @@ ${expression ? 'Expression: "' + expression + '"\n\n' : ""}`, el);
|
||||
el.__addedByMorph = true;
|
||||
});
|
||||
directive2("transition", ({ el, directive: directive3, component, cleanup: cleanup2 }) => {
|
||||
for (let i = 0; i < el.attributes.length; i++) {
|
||||
if (el.attributes[i].name.startsWith("wire:show")) {
|
||||
module_default.bind(el, {
|
||||
[directive3.rawName.replace("wire:transition", "x-transition")]: directive3.expression
|
||||
});
|
||||
return;
|
||||
}
|
||||
}
|
||||
let visibility = module_default.reactive({ state: el.__addedByMorph ? false : true });
|
||||
module_default.bind(el, {
|
||||
[directive3.rawName.replace("wire:", "x-")]: "",
|
||||
@@ -9466,6 +9593,55 @@ ${expression ? 'Expression: "' + expression + '"\n\n' : ""}`, el);
|
||||
};
|
||||
});
|
||||
|
||||
// js/directives/wire-current.js
|
||||
module_default.addInitSelector(() => `[wire\\:current]`);
|
||||
var onPageChanges = /* @__PURE__ */ new Map();
|
||||
document.addEventListener("livewire:navigated", () => {
|
||||
onPageChanges.forEach((i) => i(new URL(window.location.href)));
|
||||
});
|
||||
globalDirective("current", ({ el, directive: directive3, cleanup: cleanup2 }) => {
|
||||
let expression = directive3.expression;
|
||||
let options = {
|
||||
exact: directive3.modifiers.includes("exact"),
|
||||
strict: directive3.modifiers.includes("strict")
|
||||
};
|
||||
if (expression.startsWith("#"))
|
||||
return;
|
||||
if (!el.hasAttribute("href"))
|
||||
return;
|
||||
let href = el.getAttribute("href");
|
||||
let hrefUrl = new URL(href, window.location.href);
|
||||
let classes = expression.split(" ").filter(String);
|
||||
let refreshCurrent = (url) => {
|
||||
if (pathMatches(hrefUrl, url, options)) {
|
||||
el.classList.add(...classes);
|
||||
el.setAttribute("data-current", "");
|
||||
} else {
|
||||
el.classList.remove(...classes);
|
||||
el.removeAttribute("data-current");
|
||||
}
|
||||
};
|
||||
refreshCurrent(new URL(window.location.href));
|
||||
onPageChanges.set(el, refreshCurrent);
|
||||
cleanup2(() => onPageChanges.delete(el));
|
||||
});
|
||||
function pathMatches(hrefUrl, actualUrl, options) {
|
||||
if (hrefUrl.hostname !== actualUrl.hostname)
|
||||
return false;
|
||||
let hrefPath = options.strict ? hrefUrl.pathname : hrefUrl.pathname.replace(/\/+$/, "");
|
||||
let actualPath = options.strict ? actualUrl.pathname : actualUrl.pathname.replace(/\/+$/, "");
|
||||
if (options.exact) {
|
||||
return hrefPath === actualPath;
|
||||
}
|
||||
let hrefPathSegments = hrefPath.split("/");
|
||||
let actualPathSegments = actualPath.split("/");
|
||||
for (let i = 0; i < hrefPathSegments.length; i++) {
|
||||
if (hrefPathSegments[i] !== actualPathSegments[i])
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
// js/directives/shared.js
|
||||
function toggleBooleanStateDirective(el, directive3, isTruthy, cachedDisplay = null) {
|
||||
isTruthy = directive3.modifiers.includes("remove") ? !isTruthy : isTruthy;
|
||||
@@ -9730,11 +9906,20 @@ ${expression ? 'Expression: "' + expression + '"\n\n' : ""}`, el);
|
||||
directive2("ignore", ({ el, directive: directive3 }) => {
|
||||
if (directive3.modifiers.includes("self")) {
|
||||
el.__livewire_ignore_self = true;
|
||||
} else if (directive3.modifiers.includes("children")) {
|
||||
el.__livewire_ignore_children = true;
|
||||
} else {
|
||||
el.__livewire_ignore = true;
|
||||
}
|
||||
});
|
||||
|
||||
// js/directives/wire-cloak.js
|
||||
module_default.interceptInit((el) => {
|
||||
if (el.hasAttribute("wire:cloak")) {
|
||||
module_default.mutateDom(() => el.removeAttribute("wire:cloak"));
|
||||
}
|
||||
});
|
||||
|
||||
// js/directives/wire-dirty.js
|
||||
var refreshDirtyStatesByComponent = new WeakBag();
|
||||
on2("commit", ({ component, respond }) => {
|
||||
@@ -9746,7 +9931,6 @@ ${expression ? 'Expression: "' + expression + '"\n\n' : ""}`, el);
|
||||
});
|
||||
directive2("dirty", ({ el, directive: directive3, component }) => {
|
||||
let targets = dirtyTargets(el);
|
||||
let dirty = Alpine.reactive({ state: false });
|
||||
let oldIsDirty = false;
|
||||
let initialDisplay = el.style.display;
|
||||
let refreshDirtyState = (isDirty) => {
|
||||
@@ -9965,6 +10149,38 @@ ${expression ? 'Expression: "' + expression + '"\n\n' : ""}`, el);
|
||||
return durationInMilliSeconds || defaultDuration;
|
||||
}
|
||||
|
||||
// js/directives/wire-show.js
|
||||
module_default.interceptInit((el) => {
|
||||
for (let i = 0; i < el.attributes.length; i++) {
|
||||
if (el.attributes[i].name.startsWith("wire:show")) {
|
||||
let { name, value } = el.attributes[i];
|
||||
let modifierString = name.split("wire:show")[1];
|
||||
let expression = value.startsWith("!") ? "!$wire." + value.slice(1).trim() : "$wire." + value.trim();
|
||||
module_default.bind(el, {
|
||||
["x-show" + modifierString]() {
|
||||
return module_default.evaluate(el, expression);
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
// js/directives/wire-text.js
|
||||
module_default.interceptInit((el) => {
|
||||
for (let i = 0; i < el.attributes.length; i++) {
|
||||
if (el.attributes[i].name.startsWith("wire:text")) {
|
||||
let { name, value } = el.attributes[i];
|
||||
let modifierString = name.split("wire:text")[1];
|
||||
let expression = value.startsWith("!") ? "!$wire." + value.slice(1).trim() : "$wire." + value.trim();
|
||||
module_default.bind(el, {
|
||||
["x-text" + modifierString]() {
|
||||
return module_default.evaluate(el, expression);
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
// js/index.js
|
||||
var Livewire2 = {
|
||||
directive: directive2,
|
||||
|
||||
12
public/vendor/livewire/livewire.min.js
vendored
12
public/vendor/livewire/livewire.min.js
vendored
File diff suppressed because one or more lines are too long
6
public/vendor/livewire/livewire.min.js.map
vendored
6
public/vendor/livewire/livewire.min.js.map
vendored
File diff suppressed because one or more lines are too long
2
public/vendor/livewire/manifest.json
vendored
2
public/vendor/livewire/manifest.json
vendored
@@ -1,2 +1,2 @@
|
||||
|
||||
{"/livewire.js":"38dc8241"}
|
||||
{"/livewire.js":"df3a17f2"}
|
||||
|
||||
@@ -278,7 +278,7 @@ return [
|
||||
'ip' => 'IP(Block)',
|
||||
'ip_begin' => 'Begin IP',
|
||||
'ip_end' => 'End IP',
|
||||
'ip_help' => 'Fill in the ASN/begin IP + end IP/IP (Block), only one of the three',
|
||||
'ip_help' => 'Fill in the IP address or ASN, not both!',
|
||||
'status' => 'Status',
|
||||
'is_allowed' => 'Is whitelisted',
|
||||
'is_allowed_help' => 'IPs in the whitelist are not affected by the SeedBox rule',
|
||||
|
||||
@@ -14,6 +14,6 @@ return [
|
||||
'subject' => 'SeedBox record status changed',
|
||||
'body' => 'The status of your SeedBox record with ID :id was changed by :operator from :old_status to :new_status. Reason: :reason',
|
||||
],
|
||||
'is_seed_box_yes' => 'This IP is SeedBox, identified by the record with ID :id',
|
||||
'is_seed_box_yes' => 'This IP is SeedBox',
|
||||
'is_seed_box_no' => 'This IP is not SeedBox',
|
||||
];
|
||||
|
||||
@@ -281,10 +281,10 @@ return [
|
||||
'type' => '添加类型',
|
||||
'operator' => '运营商',
|
||||
'bandwidth' => '带宽(Mbps)',
|
||||
'ip' => 'IP(段)',
|
||||
'ip' => 'IP',
|
||||
'ip_begin' => '起始 IP',
|
||||
'ip_end' => '结束 IP',
|
||||
'ip_help' => '填写 ASN/起始 IP + 结束 IP/IP(段),不要同时填写',
|
||||
'ip_help' => '填写IP地址或ASN,不要同时填写',
|
||||
'status' => '状态',
|
||||
'is_allowed' => '是否白名单',
|
||||
'is_allowed_help' => '位于白名单中的 IP 不受 SeedBox 规则影响',
|
||||
|
||||
@@ -14,6 +14,6 @@ return [
|
||||
'subject' => 'SeedBox 记录状态变更',
|
||||
'body' => '你的 ID 为 :id 的 SeedBox 记录状态被 :operator 由 :old_status 变更为 :new_status。原因::reason',
|
||||
],
|
||||
'is_seed_box_yes' => '此 IP 是 SeedBox, 由 ID 为 :id 的记录确定',
|
||||
'is_seed_box_no' => '此 IP 不是 SeedBox',
|
||||
'is_seed_box_yes' => "此 IP 是 SeedBox",
|
||||
'is_seed_box_no' => "此 IP 不是 SeedBox",
|
||||
];
|
||||
|
||||
@@ -278,7 +278,7 @@ return [
|
||||
'ip' => 'IP(段)',
|
||||
'ip_begin' => '起始 IP',
|
||||
'ip_end' => '結束 IP',
|
||||
'ip_help' => '填寫ASN/起始 IP + 結束 IP/IP(段),不要同時填寫',
|
||||
'ip_help' => '填寫IP地址或ASN,不要同時填寫',
|
||||
'status' => '狀態',
|
||||
'is_allowed' => '是否白名單',
|
||||
'is_allowed_help' => '位於白名單中的 IP 不受 SeedBox 規則影響',
|
||||
|
||||
@@ -14,6 +14,6 @@ return [
|
||||
'subject' => 'SeedBox 記錄狀態變更',
|
||||
'body' => '你的 ID 為 :id 的 SeedBox 記錄狀態被 :operator 由 :old_status 變更為 :new_status。原因::reason',
|
||||
],
|
||||
'is_seed_box_yes' => '此 IP 是 SeedBox, 由 ID 為 :id 的記錄確定',
|
||||
'is_seed_box_yes' => '此 IP 是 SeedBox',
|
||||
'is_seed_box_no' => '此 IP 不是 SeedBox',
|
||||
];
|
||||
|
||||
Reference in New Issue
Block a user