mirror of
https://github.com/lkddi/nexusphp.git
synced 2026-04-05 07:20:58 +08:00
seed bonus add log etc
This commit is contained in:
@@ -71,6 +71,9 @@ RUN sed -i \
|
||||
|
||||
# 配置 PHP 错误日志输出到 stderr
|
||||
RUN echo "error_log = /dev/stderr" >> /usr/local/etc/php/conf.d/error-logging.ini
|
||||
# 上传配置
|
||||
RUN echo "upload_max_filesize = 10M" >> /usr/local/etc/php/conf.d/upload.ini
|
||||
RUN echo "post_max_size = 10M" >> /usr/local/etc/php/conf.d/upload.ini
|
||||
|
||||
# 安装 Composer
|
||||
RUN curl -sS https://getcomposer.org/installer | php -- --install-dir=/usr/local/bin --filename=composer
|
||||
|
||||
@@ -42,10 +42,10 @@ class HitAndRunUpdateStatus extends Command
|
||||
$torrentId = $this->option('torrent_id');
|
||||
$ignoreTime = $this->option('ignore_time');
|
||||
$rep = new HitAndRunRepository();
|
||||
$result = $rep->cronjobUpdateStatus($uid, $torrentId, $ignoreTime);
|
||||
$rep->cronjobUpdateStatus($uid, $torrentId, $ignoreTime);
|
||||
$log = sprintf(
|
||||
'[%s], %s, uid: %s, torrentId: %s, ignoreTime: %s, result: %s',
|
||||
nexus()->getRequestId(), __METHOD__, $uid, $torrentId, $ignoreTime, var_export($result, true)
|
||||
'[%s], %s, uid: %s, torrentId: %s, ignoreTime: %s',
|
||||
nexus()->getRequestId(), __METHOD__, $uid, $torrentId, $ignoreTime
|
||||
);
|
||||
$this->info($log);
|
||||
do_log($log);
|
||||
|
||||
@@ -14,6 +14,7 @@ use App\Models\TorrentExtra;
|
||||
use App\Models\User;
|
||||
use App\Repositories\ClaimRepository;
|
||||
use App\Repositories\ExamRepository;
|
||||
use App\Repositories\RequireSeedTorrentRepository;
|
||||
use App\Repositories\SeedBoxRepository;
|
||||
use App\Repositories\TokenRepository;
|
||||
use App\Repositories\UploadRepository;
|
||||
@@ -30,6 +31,8 @@ use NexusPlugin\StickyPromotion\Models\StickyPromotion;
|
||||
use NexusPlugin\StickyPromotion\Models\StickyPromotionParticipator;
|
||||
use NexusPlugin\Work\Models\RoleWork;
|
||||
use NexusPlugin\Work\WorkRepository;
|
||||
use Rhilip\Bencode\Bencode;
|
||||
use Rhilip\Bencode\TorrentFile;
|
||||
use Stichoza\GoogleTranslate\GoogleTranslate;
|
||||
|
||||
class Test extends Command
|
||||
@@ -65,22 +68,9 @@ class Test extends Command
|
||||
*/
|
||||
public function handle()
|
||||
{
|
||||
// $failedJob = DB::table('failed_jobs')->find(569);
|
||||
//
|
||||
// $payload = json_decode($failedJob->payload, true);
|
||||
// dd($payload);
|
||||
//
|
||||
// $base64 = $payload['data']['command'];
|
||||
// $job = unserialize($base64);
|
||||
//
|
||||
// dd($job);
|
||||
|
||||
// UpdateUserDownloadPrivilege::dispatch(1, "yes", "test_key");
|
||||
// $res = unserialize("O:36:\"App\\Jobs\\UpdateUserDownloadPrivilege\":3:{s:6:\"userId\";i:1;s:6:\"status\";s:3:\"yes\";s:9:\"reasonKey\";s:8:\"test_key\";}");
|
||||
// $res = unserialize("O:36:\"App\\Jobs\\UpdateUserDownloadPrivilege\":3:{s:6:\"userId\";i:1;s:6:\"status\";s:3:\"yes\";s:9:\"reasonKey\";s:8:\"test_key\";}");
|
||||
// dd($res);
|
||||
$r = TokenRepository::listUserTokenPermissionAllowed();
|
||||
dd($r);
|
||||
$rep = new RequireSeedTorrentRepository();
|
||||
// $rep->doRemove(Torrent::query()->whereIn('id', [58])->get());
|
||||
$rep->autoAddToListCronjob();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -137,7 +137,7 @@ class PluginStoreResource extends Resource
|
||||
{
|
||||
$result = [];
|
||||
$result[] = nexus_trans("plugin.labels.config_plugin_address");
|
||||
$result[] = sprintf("<code>composer config repositories.%s git %s</code>", $record->plugin_id, $record->remote_url);
|
||||
$result[] = sprintf("<code>composer config repositories.%s vcs %s</code>", $record->plugin_id, $record->remote_url);
|
||||
$result[] = "<br/>" . nexus_trans("plugin.labels.download_specific_version");
|
||||
$result[] = sprintf("<code>composer require %s:%s</code>", $record->package_name, $record->version);
|
||||
$result[] = "<br/>" . nexus_trans("plugin.labels.execute_install");
|
||||
|
||||
@@ -8,6 +8,8 @@ use App\Filament\Resources\System\SettingResource;
|
||||
use App\Models\HitAndRun;
|
||||
use App\Models\SearchBox;
|
||||
use App\Models\Setting;
|
||||
use App\Models\Tag;
|
||||
use App\Models\Torrent;
|
||||
use App\Models\User;
|
||||
use App\Repositories\TokenRepository;
|
||||
use App\Repositories\ToolRepository;
|
||||
@@ -112,6 +114,11 @@ class EditSetting extends Page implements Forms\Contracts\HasForms
|
||||
->schema($this->getHitAndRunSchema())
|
||||
->columns(2)
|
||||
;
|
||||
// $tabs[] = Forms\Components\Tabs\Tab::make(__('label.setting.require_seed_section.tab_header'))
|
||||
// ->id('require_seed_section')
|
||||
// ->schema($this->getRequireSeedSectionSchema())
|
||||
// ->columns(2)
|
||||
// ;
|
||||
|
||||
$tabs[] = Forms\Components\Tabs\Tab::make(__('label.setting.backup.tab_header'))
|
||||
->id('backup')
|
||||
@@ -220,6 +227,56 @@ class EditSetting extends Page implements Forms\Contracts\HasForms
|
||||
return apply_filter("hit_and_run_setting_schema", $default);
|
||||
}
|
||||
|
||||
private function getRequireSeedSectionSchema(): array
|
||||
{
|
||||
return [
|
||||
Forms\Components\Radio::make('require_seed_section.enabled')->options(self::$yesOrNo)->label(__('label.enabled'))->helperText(__('label.setting.require_seed_section.enabled_help')),
|
||||
Forms\Components\TextInput::make('require_seed_section.menu_title')->label(__('label.setting.require_seed_section.menu_title'))->helperText(__('label.setting.require_seed_section.menu_title_help')),
|
||||
Forms\Components\TextInput::make('require_seed_section.seeder_lte')->label(__('label.setting.require_seed_section.seeder_lte'))->helperText(__('label.setting.require_seed_section.seeder_lte_help'))->integer(),
|
||||
Forms\Components\TextInput::make('require_seed_section.seeder_gte')->label(__('label.setting.require_seed_section.seeder_gte'))->helperText(__('label.setting.require_seed_section.seeder_gte_help'))->integer(),
|
||||
Forms\Components\CheckboxList::make('require_seed_section.require_tags')->label(__('label.setting.require_seed_section.require_tags'))->helperText(__('label.setting.require_seed_section.require_tags_help'))->options(Tag::query()->pluck('name', 'id'))->columns(4),
|
||||
Forms\Components\Select::make('require_seed_section.promotion_state')->label(__('label.setting.require_seed_section.promotion_state'))->helperText(__('label.setting.require_seed_section.promotion_state_help'))->options(Torrent::listPromotionTypes(true)),
|
||||
Forms\Components\TextInput::make('require_seed_section.daily_seed_time_min')->label(__('label.setting.require_seed_section.daily_seed_time_min'))->helperText(__('label.setting.require_seed_section.daily_seed_time_min_help'))->integer(),
|
||||
Forms\Components\TextInput::make('require_seed_section.torrent_count_max')->label(__('label.setting.require_seed_section.torrent_count_max'))->helperText(__('label.setting.require_seed_section.torrent_count_max_help'))->integer(),
|
||||
Forms\Components\Repeater::make('require_seed_section.bonus_reward')
|
||||
->label(__('label.setting.require_seed_section.bonus_reward'))
|
||||
->helperText(__('label.setting.require_seed_section.bonus_reward_help'))
|
||||
->schema([
|
||||
Forms\Components\TextInput::make('seeders')
|
||||
->label(__('label.setting.require_seed_section.seeders'))
|
||||
->required()
|
||||
->integer()
|
||||
->columnSpan(2)
|
||||
,
|
||||
Forms\Components\Repeater::make('seed_time_reward')
|
||||
->label(__('label.setting.require_seed_section.seed_time_reward'))
|
||||
->schema([
|
||||
Forms\Components\TextInput::make('begin')->label(__('label.setting.require_seed_section.seed_time_reward_begin'))->helperText(__('label.setting.require_seed_section.seed_time_reward_begin_help')),
|
||||
Forms\Components\TextInput::make('end')->label(__('label.setting.require_seed_section.seed_time_reward_end'))->helperText(__('label.setting.require_seed_section.seed_time_reward_end_help')),
|
||||
Forms\Components\TextInput::make('window')->label(__('label.setting.require_seed_section.seed_time_reward_window'))->helperText(__('label.setting.require_seed_section.seed_time_reward_window_help')),
|
||||
Forms\Components\TextInput::make('reward')->label(__('label.setting.require_seed_section.seed_time_reward_reward'))->helperText(__('label.setting.require_seed_section.seed_time_reward_reward_help')),
|
||||
])
|
||||
->columns(4)
|
||||
->columnSpan(5)
|
||||
,
|
||||
Forms\Components\Repeater::make('data_traffic_reward')
|
||||
->label(__('label.setting.require_seed_section.data_traffic_reward'))
|
||||
->schema([
|
||||
Forms\Components\TextInput::make('begin')->label(__('label.setting.require_seed_section.data_traffic_reward_begin'))->helperText(__('label.setting.require_seed_section.data_traffic_reward_begin_help')),
|
||||
Forms\Components\TextInput::make('end')->label(__('label.setting.require_seed_section.data_traffic_reward_end'))->helperText(__('label.setting.require_seed_section.data_traffic_reward_end_help')),
|
||||
Forms\Components\TextInput::make('window')->label(__('label.setting.require_seed_section.data_traffic_reward_window'))->helperText(__('label.setting.require_seed_section.data_traffic_reward_window_help')),
|
||||
Forms\Components\TextInput::make('reward')->label(__('label.setting.require_seed_section.data_traffic_reward_reward'))->helperText(__('label.setting.require_seed_section.data_traffic_reward_reward_help')),
|
||||
])
|
||||
->columns(4)
|
||||
->columnSpan(5)
|
||||
])
|
||||
->columns(12)
|
||||
->columnSpanFull()
|
||||
->defaultItems(3)
|
||||
->reorderable(false)
|
||||
];
|
||||
}
|
||||
|
||||
private function getTabMeilisearchSchema($id): array
|
||||
{
|
||||
$schema = [];
|
||||
|
||||
@@ -169,6 +169,7 @@ class AnnounceLogResource extends Resource
|
||||
Forms\Components\TextInput::make('user_id')
|
||||
->label(__('announce-log.user_id'))
|
||||
->numeric()
|
||||
->minValue(1)
|
||||
,
|
||||
])
|
||||
,
|
||||
@@ -177,6 +178,7 @@ class AnnounceLogResource extends Resource
|
||||
Forms\Components\TextInput::make('torrent_id')
|
||||
->label(__('announce-log.torrent_id'))
|
||||
->numeric()
|
||||
->minValue(1)
|
||||
,
|
||||
])
|
||||
,
|
||||
|
||||
@@ -55,7 +55,7 @@ class BonusLogResource extends Resource
|
||||
,
|
||||
Tables\Columns\TextColumn::make('old_total_value')
|
||||
->label(__('bonus-log.fields.old_total_value'))
|
||||
->formatStateUsing(fn ($state) => number_format($state))
|
||||
->formatStateUsing(fn ($state) => $state >= 0 ? number_format($state) : '-')
|
||||
,
|
||||
Tables\Columns\TextColumn::make('value')
|
||||
->formatStateUsing(fn ($record) => $record->old_total_value > $record->new_total_value ? "-" . number_format($record->value) : "+" . number_format($record->value))
|
||||
@@ -63,7 +63,7 @@ class BonusLogResource extends Resource
|
||||
,
|
||||
Tables\Columns\TextColumn::make('new_total_value')
|
||||
->label(__('bonus-log.fields.new_total_value'))
|
||||
->formatStateUsing(fn ($state) => number_format($state))
|
||||
->formatStateUsing(fn ($state) => $state >= 0 ? number_format($state) : '-')
|
||||
,
|
||||
Tables\Columns\TextColumn::make('comment')
|
||||
->label(__('label.comment'))
|
||||
|
||||
@@ -2,6 +2,7 @@
|
||||
|
||||
namespace App\Jobs;
|
||||
|
||||
use App\Models\BonusLogs;
|
||||
use App\Models\Setting;
|
||||
use App\Models\User;
|
||||
use Illuminate\Bus\Queueable;
|
||||
@@ -86,22 +87,33 @@ class CalculateUserSeedBonus implements ShouldQueue
|
||||
}
|
||||
$sql = sprintf("select %s from users where id in (%s)", implode(',', User::$commonFields), $idStr);
|
||||
$results = NexusDB::select($sql);
|
||||
if (empty($results)) {
|
||||
do_log("$logPrefix, no data from idStr: $idStr", "error");
|
||||
return;
|
||||
}
|
||||
$logFile = getLogFile("seed-bonus-points");
|
||||
do_log("$logPrefix, [GET_UID_REAL], count: " . count($results) . ", logFile: $logFile");
|
||||
$fd = fopen($logFile, 'a');
|
||||
$seedPointsUpdates = $seedPointsPerHourUpdates = $seedBonusUpdates = [];
|
||||
$seedingTorrentCountUpdates = $seedingTorrentSizeUpdates = [];
|
||||
$logStr = "";
|
||||
$bonusLogInsert = [];
|
||||
foreach ($results as $userInfo)
|
||||
{
|
||||
$uid = $userInfo['id'];
|
||||
$donorAddition = $officialAddition = $haremAddition = $medalAddition = 0;
|
||||
$isDonor = is_donor($userInfo);
|
||||
$seedBonusResult = calculate_seed_bonus($uid);
|
||||
$bonusLog = "[CLEANUP_CLI_CALCULATE_SEED_BONUS_HANDLE_USER], user: $uid, seedBonusResult: " . nexus_json_encode($seedBonusResult);
|
||||
$all_bonus = $seedBonusResult['seed_bonus'];
|
||||
$all_bonus = $basicBonus = $seedBonusResult['seed_bonus'];
|
||||
$bonusLog .= ", all_bonus: $all_bonus";
|
||||
if ($all_bonus == 0) {
|
||||
do_log("$bonusLog, all_bonus is zero, skip");
|
||||
continue;
|
||||
}
|
||||
if ($isDonor && $donortimes_bonus != 0) {
|
||||
$all_bonus = $all_bonus * $donortimes_bonus;
|
||||
$donorAddition = $basicBonus * $donortimes_bonus;
|
||||
$all_bonus += $donorAddition;
|
||||
$bonusLog .= ", isDonor, donortimes_bonus: $donortimes_bonus, all_bonus: $all_bonus";
|
||||
}
|
||||
if ($officialAdditionFactor > 0) {
|
||||
@@ -133,6 +145,14 @@ class CalculateUserSeedBonus implements ShouldQueue
|
||||
$seedingTorrentCountUpdates[] = sprintf("when %d then %f", $uid, $seedBonusResult['count']);
|
||||
$seedingTorrentSizeUpdates[] = sprintf("when %d then %f", $uid, $seedBonusResult['size']);
|
||||
$seedBonusUpdates[] = sprintf("when %d then seedbonus + %f", $uid, $all_bonus);
|
||||
//here before/after not correct, don't record it, fill with -1
|
||||
$this->appendBonusLogInsert($bonusLogInsert, $uid, [
|
||||
BonusLogs::BUSINESS_TYPE_SEEDING_BASIC => $basicBonus,
|
||||
BonusLogs::BUSINESS_TYPE_SEEDING_DONOR_ADDITION => $donorAddition,
|
||||
BonusLogs::BUSINESS_TYPE_SEEDING_OFFICIAL_ADDITION => $officialAddition,
|
||||
BonusLogs::BUSINESS_TYPE_SEEDING_HAREM_ADDITION => $haremAddition,
|
||||
BonusLogs::BUSINESS_TYPE_SEEDING_MEDAL_ADDITION => $medalAddition,
|
||||
]);
|
||||
if ($fd) {
|
||||
$log = sprintf(
|
||||
'%s|%s|%s|%s|%s|%s|%s|%s',
|
||||
@@ -155,6 +175,9 @@ class CalculateUserSeedBonus implements ShouldQueue
|
||||
if ($delIdRedisKey) {
|
||||
NexusDB::cache_del($this->idRedisKey);
|
||||
}
|
||||
if (!empty($bonusLogInsert)) {
|
||||
BonusLogs::query()->insert($bonusLogInsert);
|
||||
}
|
||||
fwrite($fd, $logStr);
|
||||
$costTime = time() - $beginTimestamp;
|
||||
do_log(sprintf(
|
||||
@@ -174,4 +197,22 @@ class CalculateUserSeedBonus implements ShouldQueue
|
||||
{
|
||||
do_log("failed: " . $exception->getMessage() . $exception->getTraceAsString(), 'error');
|
||||
}
|
||||
|
||||
private function appendBonusLogInsert(array &$bonusLogInsert, int $userId, array $typeValues): void
|
||||
{
|
||||
foreach ($typeValues as $type => $value) {
|
||||
if ($value > 0) {
|
||||
$bonusLogInsert[] = [
|
||||
'business_type' => $type,
|
||||
'uid' => $userId,
|
||||
'old_total_value' => -1,
|
||||
'value' => $value,
|
||||
'new_total_value' => -1,
|
||||
'comment' => BonusLogs::$businessTypes[$type]['text'],
|
||||
'created_at' => now()->toDateTimeString(),
|
||||
'updated_at' => now()->toDateTimeString(),
|
||||
];
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -47,7 +47,7 @@ class FireEvent implements ShouldQueue
|
||||
}
|
||||
$result = call_user_func_array([$eventName, "dispatch"], $params);
|
||||
$log .= ", success call dispatch, result: " . var_export($result, true);
|
||||
publish_model_event($name, $model->id);
|
||||
publish_model_event($name, $model->id, $model->toJson());
|
||||
} else {
|
||||
$log .= ", no event match this name";
|
||||
}
|
||||
|
||||
@@ -89,6 +89,10 @@ class UpdateTorrentSeedersEtc implements ShouldQueue
|
||||
->whereRaw("torrent in ($idStr)")
|
||||
->groupBy(['torrent', 'seeder'])
|
||||
->get();
|
||||
if ($res->isEmpty()) {
|
||||
do_log("$logPrefix, no data from idStr: $idStr", "error");
|
||||
return;
|
||||
}
|
||||
foreach ($res as $row) {
|
||||
if ($row->seeder == "yes")
|
||||
$key = "seeders";
|
||||
|
||||
@@ -87,6 +87,10 @@ class UpdateUserSeedingLeechingTime implements ShouldQueue
|
||||
->whereRaw("userid in ($idStr)")
|
||||
->groupBy("userid")
|
||||
->get();
|
||||
if ($res->isEmpty()) {
|
||||
do_log("$logPrefix, no data from idStr: $idStr", "error");
|
||||
return;
|
||||
}
|
||||
$seedtimeUpdates = $leechTimeUpdates = [];
|
||||
$nowStr = now()->toDateTimeString();
|
||||
$count = 0;
|
||||
|
||||
@@ -47,6 +47,12 @@ class BonusLogs extends NexusModel
|
||||
const BUSINESS_TYPE_RECEIVE_GIFT = 1003;
|
||||
const BUSINESS_TYPE_UPLOAD_TORRENT = 1004;
|
||||
|
||||
const BUSINESS_TYPE_SEEDING_BASIC = 10000;
|
||||
const BUSINESS_TYPE_SEEDING_DONOR_ADDITION = 10001;
|
||||
const BUSINESS_TYPE_SEEDING_OFFICIAL_ADDITION = 10002;
|
||||
const BUSINESS_TYPE_SEEDING_HAREM_ADDITION = 10003;
|
||||
const BUSINESS_TYPE_SEEDING_MEDAL_ADDITION = 10004;
|
||||
|
||||
public static array $businessTypes = [
|
||||
self::BUSINESS_TYPE_CANCEL_HIT_AND_RUN => ['text' => 'Cancel H&R'],
|
||||
self::BUSINESS_TYPE_BUY_MEDAL => ['text' => 'Buy medal'],
|
||||
@@ -75,6 +81,11 @@ class BonusLogs extends NexusModel
|
||||
self::BUSINESS_TYPE_RECEIVE_REWARD => ['text' => 'Receive reward'],
|
||||
self::BUSINESS_TYPE_RECEIVE_GIFT => ['text' => 'Receive gift'],
|
||||
self::BUSINESS_TYPE_UPLOAD_TORRENT => ['text' => 'Upload torrent'],
|
||||
self::BUSINESS_TYPE_SEEDING_BASIC => ['text' => 'Seeding basic'],
|
||||
self::BUSINESS_TYPE_SEEDING_DONOR_ADDITION => ['text' => 'Seeding donor addition'],
|
||||
self::BUSINESS_TYPE_SEEDING_OFFICIAL_ADDITION => ['text' => 'Seeding official addition'],
|
||||
self::BUSINESS_TYPE_SEEDING_HAREM_ADDITION => ['text' => 'Seeding harem addition'],
|
||||
self::BUSINESS_TYPE_SEEDING_MEDAL_ADDITION => ['text' => 'Seeding medal addition'],
|
||||
];
|
||||
|
||||
public function getBusinessTypeTextAttribute()
|
||||
|
||||
12
app/Models/RequireSeedTorrent.php
Normal file
12
app/Models/RequireSeedTorrent.php
Normal file
@@ -0,0 +1,12 @@
|
||||
<?php
|
||||
|
||||
namespace App\Models;
|
||||
|
||||
use Illuminate\Database\Eloquent\Model;
|
||||
|
||||
class RequireSeedTorrent extends NexusModel
|
||||
{
|
||||
protected $fillable = ['torrent_id'];
|
||||
|
||||
public $timestamps = true;
|
||||
}
|
||||
@@ -263,5 +263,50 @@ class Setting extends NexusModel
|
||||
return self::get('security.record_announce_logs') == 'yes';
|
||||
}
|
||||
|
||||
public static function getIsRequireSeedSectionEnabled(): bool
|
||||
{
|
||||
return self::get("require_seed_section.enabled") == "yes";
|
||||
}
|
||||
|
||||
public static function getRequireSeedSectionSeederGte(): int
|
||||
{
|
||||
return (int)self::get("require_seed_section.seeder_gte");
|
||||
}
|
||||
|
||||
public static function getRequireSeedSectionSeederLte(): int
|
||||
{
|
||||
return (int)self::get("require_seed_section.seeder_lte");
|
||||
}
|
||||
|
||||
public static function getRequireSeedSectionMenuTitle(): string
|
||||
{
|
||||
return self::get("require_seed_section.menu_title", nexus_trans("torrent.require_seed_section_menu_title"));
|
||||
}
|
||||
|
||||
public static function getRequireSeedSectionPromotionState(): int
|
||||
{
|
||||
return self::get("require_seed_section.promotion_state", Torrent::REQUIRE_SEED_SECTION_DEFAULT_PROMOTION_STATE);
|
||||
}
|
||||
|
||||
public static function getRequireSeedSectionBonusAdditionFactor(): float
|
||||
{
|
||||
return self::get("require_seed_section.bonus_addition_factor", Torrent::REQUIRE_SEED_SECTION_DEFAULT_BONUS_ADDITION_FACTOR);
|
||||
}
|
||||
|
||||
public static function getRequireSeedSectionTags(): array
|
||||
{
|
||||
return self::get("require_seed_section.require_tags", []);
|
||||
}
|
||||
|
||||
public static function getRequireSeedSectionTorrentCountMax(): int
|
||||
{
|
||||
return self::get("require_seed_section.torrent_count_max", Torrent::REQUIRE_SEED_SECTION_DEFAULT_TORRENT_COUNT_MAX);
|
||||
}
|
||||
|
||||
public static function getBonusMinSize(): int
|
||||
{
|
||||
return (int)self::get("bonus.min_size");
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
||||
@@ -170,6 +170,12 @@ class Torrent extends NexusModel
|
||||
|
||||
const NFO_VIEW_STYLE_DOS = 'magic';
|
||||
const NFO_VIEW_STYLE_WINDOWS = 'latin-1';
|
||||
const REQUIRE_SEED_SECTION_DEFAULT_PROMOTION_STATE = self::PROMOTION_FREE;
|
||||
const REQUIRE_SEED_SECTION_DEFAULT_BONUS_ADDITION_FACTOR = 0;
|
||||
const REQUIRE_SEED_SECTION_DEFAULT_TORRENT_COUNT_MAX = 100;
|
||||
const REQUIRE_SEED_SECTION_PROMOTION_STATE_CACHE_KEY = "REQUIRE_SEED_SECTION_PROMOTION_STATE_CACHE";
|
||||
const REQUIRE_SEED_SECTION_TORRENT_ON_LIST_CACHE_KEY = "REQUIRE_SEED_SECTION_TORRENT_ON_LIST_CACHE";
|
||||
const REQUIRE_SEED_SECTION_TORRENT_USER_CACHE_KEY = "REQUIRE_SEED_SECTION_TORRENT_USER_CACHE";
|
||||
|
||||
public static array $nfoViewStyles = [
|
||||
self::NFO_VIEW_STYLE_DOS => ['text' => 'DOS-vy'],
|
||||
|
||||
@@ -19,22 +19,7 @@ class TrackerUrl extends NexusModel
|
||||
if ($model->is_default == 1) {
|
||||
self::query()->where("id", "!=", $model->id)->update(["is_default" => 0]);
|
||||
}
|
||||
//添加 id 与 URL 映射
|
||||
$redis = NexusDB::redis();
|
||||
$redis->del(self::TRACKER_URL_CACHE_KEY);
|
||||
$list = self::listAll();
|
||||
$first = $list->first();
|
||||
$hasDefault = false;
|
||||
foreach ($list as $item) {
|
||||
$redis->hset(self::TRACKER_URL_CACHE_KEY, $item->id, $item->url);
|
||||
if ($item->is_default == 1) {
|
||||
$hasDefault = true;
|
||||
$redis->set(self::TRACKER_URL_DEFAULT_CACHE_KEY, $item->url);
|
||||
}
|
||||
}
|
||||
if (!$hasDefault && $first) {
|
||||
$redis->set(self::TRACKER_URL_DEFAULT_CACHE_KEY, $first->url);
|
||||
}
|
||||
self::saveUrlCache();
|
||||
});
|
||||
static::saving(function (TrackerUrl $model) {
|
||||
if ($model->is_default == 1) {
|
||||
@@ -43,6 +28,26 @@ class TrackerUrl extends NexusModel
|
||||
});
|
||||
}
|
||||
|
||||
public static function saveUrlCache(): void
|
||||
{
|
||||
//添加 id 与 URL 映射
|
||||
$redis = NexusDB::redis();
|
||||
$redis->del(self::TRACKER_URL_CACHE_KEY);
|
||||
$list = self::listAll();
|
||||
$first = $list->first();
|
||||
$hasDefault = false;
|
||||
foreach ($list as $item) {
|
||||
$redis->hset(self::TRACKER_URL_CACHE_KEY, $item->id, $item->url);
|
||||
if ($item->is_default == 1) {
|
||||
$hasDefault = true;
|
||||
$redis->set(self::TRACKER_URL_DEFAULT_CACHE_KEY, $item->url);
|
||||
}
|
||||
}
|
||||
if (!$hasDefault && $first) {
|
||||
$redis->set(self::TRACKER_URL_DEFAULT_CACHE_KEY, $first->url);
|
||||
}
|
||||
}
|
||||
|
||||
public static function listAll()
|
||||
{
|
||||
return self::query()
|
||||
|
||||
@@ -66,7 +66,7 @@ class User extends Authenticatable implements FilamentUser, HasName
|
||||
self::CLASS_EXTREME_USER => ['text' => 'Extreme User', 'min_seed_points' => 600000],
|
||||
self::CLASS_ULTIMATE_USER => ['text' => 'Ultimate User', 'min_seed_points' => 800000],
|
||||
self::CLASS_NEXUS_MASTER => ['text' => 'Nexus Master', 'min_seed_points' => 1000000],
|
||||
self::CLASS_VIP => ['text' => 'Vip'],
|
||||
self::CLASS_VIP => ['text' => 'VIP'],
|
||||
self::CLASS_RETIREE => ['text' => 'Retiree'],
|
||||
self::CLASS_UPLOADER => ['text' => 'Uploader'],
|
||||
self::CLASS_MODERATOR => ['text' => 'Moderator'],
|
||||
@@ -105,6 +105,13 @@ class User extends Authenticatable implements FilamentUser, HasName
|
||||
|
||||
public static array $notificationOptions = ['topic_reply', 'hr_reached'];
|
||||
|
||||
private const USER_ENABLE_LATELY = "user_enable_lately:%s";
|
||||
|
||||
public static function getUserEnableLatelyCacheKey(int $userId): string
|
||||
{
|
||||
return sprintf(self::USER_ENABLE_LATELY, $userId);
|
||||
}
|
||||
|
||||
public function getClassTextAttribute(): string
|
||||
{
|
||||
return self::getClassText($this->class);
|
||||
@@ -115,12 +122,12 @@ class User extends Authenticatable implements FilamentUser, HasName
|
||||
if (!is_numeric($class)|| !isset(self::$classes[$class])) {
|
||||
return '';
|
||||
}
|
||||
$classText = self::$classes[$class]['text'];
|
||||
if ($class >= self::CLASS_VIP) {
|
||||
$classText = nexus_trans('user.class_names.' . $class);
|
||||
$alias = nexus_trans('user.class_names.' . $class);
|
||||
} else {
|
||||
$classText = self::$classes[$class]['text'];
|
||||
$alias = Setting::get("account.{$class}_alias");
|
||||
}
|
||||
$alias = Setting::get("account.{$class}_alias");
|
||||
if (!empty($alias)) {
|
||||
$classText .= "({$alias})";
|
||||
}
|
||||
|
||||
10
app/Models/UserRequireSeedTorrent.php
Normal file
10
app/Models/UserRequireSeedTorrent.php
Normal file
@@ -0,0 +1,10 @@
|
||||
<?php
|
||||
|
||||
namespace App\Models;
|
||||
|
||||
class UserRequireSeedTorrent extends NexusModel
|
||||
{
|
||||
protected $fillable = ['user_id', 'torrent_id', 'seed_time_begin', 'uploaded_begin', 'last_settlement_at'];
|
||||
|
||||
public $timestamps = true;
|
||||
}
|
||||
@@ -216,8 +216,8 @@ class HitAndRunRepository extends BaseRepository
|
||||
|
||||
//check leech time
|
||||
if (isset($setting['leech_time_minimum']) && $setting['leech_time_minimum'] > 0) {
|
||||
// $targetLeechTime = $row->snatch->leechtime;
|
||||
$targetLeechTime = $row->leech_time_no_seeder;//使用自身记录的值
|
||||
//use diff, other index should do also, update later @todo
|
||||
$targetLeechTime = $row->snatch->leech_time_no_seeder - $row->leech_time_no_seeder_begin;
|
||||
$requireLeechTime = bcmul($setting['leech_time_minimum'], 3600);
|
||||
do_log("$currentLog, targetLeechTime: $targetLeechTime, requireLeechTime: $requireLeechTime");
|
||||
if ($targetLeechTime >= $requireLeechTime) {
|
||||
@@ -258,12 +258,13 @@ class HitAndRunRepository extends BaseRepository
|
||||
|
||||
private function geReachedMessage(HitAndRun $hitAndRun): array
|
||||
{
|
||||
$snatched = $hitAndRun->snatch;
|
||||
return [
|
||||
'receiver' => $hitAndRun->uid,
|
||||
'added' => Carbon::now()->toDateTimeString(),
|
||||
'subject' => nexus_trans('hr.reached_message_subject', ['hit_and_run_id' => $hitAndRun->id], $hitAndRun->user->locale),
|
||||
'msg' => nexus_trans('hr.reached_message_content', [
|
||||
'completed_at' => format_datetime($hitAndRun->snatch->completedat),
|
||||
'completed_at' => format_datetime($snatched->completedat ?: $snatched->startdat),
|
||||
'torrent_id' => $hitAndRun->torrent_id,
|
||||
'torrent_name' => $hitAndRun->torrent->name,
|
||||
], $hitAndRun->user->locale),
|
||||
@@ -305,7 +306,7 @@ class HitAndRunRepository extends BaseRepository
|
||||
do_log(__METHOD__);
|
||||
$comment = nexus_trans('hr.reached_by_leech_time_comment', [
|
||||
'now' => Carbon::now()->toDateTimeString(),
|
||||
'leech_time' => bcdiv($hitAndRun->snatch->leechtime, 3600, 1),
|
||||
'leech_time' => bcdiv($hitAndRun->snatch->leech_time_no_seeder - $hitAndRun->leech_time_no_seeder_begin, 3600, 1),
|
||||
'leech_time_minimum' => $setting['leech_time_minimum'],
|
||||
], $hitAndRun->user->locale);
|
||||
$update = [
|
||||
|
||||
169
app/Repositories/RequireSeedTorrentRepository.php
Normal file
169
app/Repositories/RequireSeedTorrentRepository.php
Normal file
@@ -0,0 +1,169 @@
|
||||
<?php
|
||||
|
||||
namespace App\Repositories;
|
||||
|
||||
use App\Models\RequireSeedTorrent;
|
||||
use App\Models\Setting;
|
||||
use App\Models\Torrent;
|
||||
use App\Models\UserRequireSeedTorrent;
|
||||
use Illuminate\Support\Collection;
|
||||
use Nexus\Database\NexusDB;
|
||||
|
||||
class RequireSeedTorrentRepository extends BaseRepository
|
||||
{
|
||||
public function autoAddToListCronjob(): void
|
||||
{
|
||||
$logPrefix = "[RequireSeedTorrentRepository.autoAddToListCronjob]";
|
||||
$enabled = Setting::getIsRequireSeedSectionEnabled();
|
||||
if (!$enabled) {
|
||||
do_log("$logPrefix, not enabled");
|
||||
return;
|
||||
}
|
||||
$countMaxAllowed = Setting::getRequireSeedSectionTorrentCountMax();
|
||||
$countNow = RequireSeedTorrent::query()->count();
|
||||
if ($countNow >= $countMaxAllowed) {
|
||||
do_log("$logPrefix, max allowed $countMaxAllowed reached");
|
||||
return;
|
||||
}
|
||||
$count = $countMaxAllowed - $countNow;
|
||||
$seederMax = Setting::getRequireSeedSectionSeederLte();
|
||||
$seederMin = Setting::getRequireSeedSectionSeederGte();
|
||||
$logPrefix .= ", countMaxAllowed: $countMaxAllowed, countNow: $countNow, count: $count, seederMax: $seederMax, seederMin: $seederMin";
|
||||
$query = Torrent::query()->where('banned', 'no')
|
||||
->where('seeders', '<=', $seederMax)
|
||||
->where('seeders', '>=', $seederMin)
|
||||
;
|
||||
$tags = Setting::getRequireSeedSectionTags();
|
||||
if (!empty($tags)) {
|
||||
$logPrefix .= ", tags: " . implode(',', $tags);
|
||||
$query->whereHas('torrent_tags', function ($query) use ($tags) {
|
||||
$query->whereIn('tag_id', $tags);
|
||||
});
|
||||
}
|
||||
$list = $query->leftJoin("require_seed_torrents", "torrents.id", "=", "require_seed_torrents.torrent_id")
|
||||
->whereNull("require_seed_torrents.id")
|
||||
->orderBy('torrents.seeders', 'asc')
|
||||
->orderBy('torrents.times_completed', 'desc')
|
||||
->orderBy('torrents.hits', 'desc')
|
||||
->limit($count)
|
||||
->get(['torrents.id'])
|
||||
;
|
||||
$data = [];
|
||||
$nowStr = now()->toDateTimeString();
|
||||
$redis = NexusDB::redis();
|
||||
$cacheKey = self::getTorrentCacheKey();
|
||||
foreach ($list as $item) {
|
||||
$data[] = [
|
||||
'torrent_id' => $item->id,
|
||||
'created_at' => $nowStr,
|
||||
'updated_at' => $nowStr,
|
||||
];
|
||||
$redis->hset($cacheKey, $item->id, $nowStr);
|
||||
}
|
||||
RequireSeedTorrent::query()->insert($data);
|
||||
do_log("$logPrefix, success inserted: " . count($data));
|
||||
}
|
||||
|
||||
public function autoRemoveFromListCronjob(): void
|
||||
{
|
||||
$idArr = RequireSeedTorrent::query()->pluck('torrent_id')->toArray();
|
||||
if (empty($idArr)) {
|
||||
do_log("no data to remove");
|
||||
return;
|
||||
}
|
||||
$seederMax = Setting::getRequireSeedSectionSeederLte();
|
||||
$seederMin = Setting::getRequireSeedSectionSeederGte();
|
||||
$torrents = Torrent::query()->whereIn('id', $idArr)
|
||||
->where('seeders', '<', $seederMin)
|
||||
->get(['id'])
|
||||
;
|
||||
if (!empty($torrents)) {
|
||||
$this->doRemove($torrents);
|
||||
do_log(sprintf("remove %s seeders < %s", count($torrents), $seederMin));
|
||||
}
|
||||
$torrents = Torrent::query()->whereIn('id', $idArr)
|
||||
->where('seeders', '>', $seederMax)
|
||||
->get(['id'])
|
||||
;
|
||||
if (!empty($torrents)) {
|
||||
$this->doRemove($torrents);
|
||||
do_log(sprintf("remove %s seeders > %s", count($torrents), $seederMax));
|
||||
}
|
||||
}
|
||||
|
||||
public function doRemove(Collection $torrents): void
|
||||
{
|
||||
$idArr = [];
|
||||
$redis = NexusDB::redis();
|
||||
$promotionState = Setting::getRequireSeedSectionPromotionState();
|
||||
$ttlInSeconds = 24 * 3600;
|
||||
foreach ($torrents as $torrent) {
|
||||
$idArr[] = $torrent->id;
|
||||
$promotionStateCacheKey = sprintf("%s:%s", Torrent::REQUIRE_SEED_SECTION_PROMOTION_STATE_CACHE_KEY, $torrent->id);
|
||||
$redis->setex($promotionStateCacheKey, $ttlInSeconds, $promotionState);
|
||||
//remove torrent from list
|
||||
$redis->hDel(self::getTorrentCacheKey(), $torrent->id);
|
||||
//remove all users under torrent
|
||||
$redis->del(self::getTorrentUserCacheKey($torrent->id));
|
||||
}
|
||||
RequireSeedTorrent::query()->whereIn('torrent_id', $idArr)->delete();
|
||||
UserRequireSeedTorrent::query()->whereIn('torrent_id', $idArr)->delete();
|
||||
do_log("success removed " . count($idArr));
|
||||
}
|
||||
|
||||
private static function getTorrentCacheKey(): string
|
||||
{
|
||||
return Torrent::REQUIRE_SEED_SECTION_TORRENT_ON_LIST_CACHE_KEY;
|
||||
}
|
||||
|
||||
private static function getTorrentUserCacheKey($torrentId): string
|
||||
{
|
||||
return sprintf("%s:%s", Torrent::REQUIRE_SEED_SECTION_TORRENT_USER_CACHE_KEY, $torrentId);
|
||||
}
|
||||
|
||||
public static function shouldRecordUser(\Redis $redis, $userId, $torrentId): bool
|
||||
{
|
||||
$logPrefix = "userId: $userId, torrentId: $torrentId";
|
||||
//check enabled or not
|
||||
if (!Setting::getIsRequireSeedSectionEnabled()) {
|
||||
do_log("$logPrefix, not enabled", 'debug');
|
||||
return false;
|
||||
}
|
||||
//first, torrent on list
|
||||
$onListCacheKey = self::getTorrentCacheKey();
|
||||
if (!$redis->hExists($onListCacheKey, $torrentId)) {
|
||||
do_log("$logPrefix, torrent not on list: $onListCacheKey", 'debug');
|
||||
return false;
|
||||
}
|
||||
//second, torrent user not exists
|
||||
$torrentUserCacheKey = self::getTorrentUserCacheKey($torrentId);
|
||||
if ($redis->hExists($torrentUserCacheKey, $userId)) {
|
||||
do_log("$logPrefix, user already exists: $torrentUserCacheKey", 'debug');
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
public static function recordUser(\Redis $redis, $userId, $torrentId, array $snatchedInfo): void
|
||||
{
|
||||
$torrentUserCacheKey = self::getTorrentUserCacheKey($torrentId);
|
||||
$nowStr = now()->toDateTimeString();
|
||||
$values = [
|
||||
'user_id' => $userId,
|
||||
'torrent_id' => $torrentId,
|
||||
'seed_time_begin' => $snatchedInfo['seedtime'],
|
||||
'uploaded_begin' => $snatchedInfo['uploaded'],
|
||||
'created_at' => $nowStr,
|
||||
];
|
||||
$uniqueBy = ['user_id', 'torrent_id'];
|
||||
$update = ['updated_at'];
|
||||
UserRequireSeedTorrent::query()->upsert($values, $uniqueBy, $update);
|
||||
$redis->hset($torrentUserCacheKey, $userId, $nowStr);
|
||||
do_log("success insert user: $userId, torrent: $torrentId");
|
||||
}
|
||||
|
||||
public function autoSettlementCronjob(): void
|
||||
{
|
||||
|
||||
}
|
||||
}
|
||||
@@ -123,9 +123,17 @@ class SeedBoxRepository extends BaseRepository
|
||||
|
||||
public function delete($id, $uid)
|
||||
{
|
||||
$baseQuery = SeedBoxRecord::query()->whereIn('id', Arr::wrap($id))->where('uid', $uid);
|
||||
$list = $baseQuery->clone()->get();
|
||||
if ($list->isEmpty()) {
|
||||
return false;
|
||||
}
|
||||
$baseQuery->delete();
|
||||
$this->clearApprovalCountCache();
|
||||
publish_model_event("seed_box_record_deleted", $id);
|
||||
return SeedBoxRecord::query()->whereIn('id', Arr::wrap($id))->where('uid', $uid)->delete();
|
||||
foreach ($list as $record) {
|
||||
publish_model_event("seed_box_record_deleted", $record->id, $record->toJson());
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
public function updateStatus(SeedBoxRecord $seedBoxRecord, $status, $reason = '')
|
||||
@@ -321,8 +329,13 @@ class SeedBoxRepository extends BaseRepository
|
||||
if (is_null($reader)) {
|
||||
return 0;
|
||||
}
|
||||
$asnObj = $reader->asn($ip);
|
||||
return $asnObj->autonomousSystemNumber ?? 0;
|
||||
try {
|
||||
$asnObj = $reader->asn($ip);
|
||||
return $asnObj->autonomousSystemNumber ?? 0;
|
||||
} catch (\Exception $e) {
|
||||
do_log("ip: $ip, error: " . $e->getMessage(), 'error');
|
||||
return 0;
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
@@ -169,6 +169,7 @@ class UploadRepository extends BaseRepository
|
||||
throw new NexusException(nexus_trans('upload.missing_torrent_file'));
|
||||
}
|
||||
if (!$file->isValid()) {
|
||||
do_log("torrent file is invalid: " . nexus_json_encode($_FILES), 'error');
|
||||
throw new NexusException("upload torrent file error");
|
||||
}
|
||||
$size = $file->getSize();
|
||||
|
||||
@@ -2,6 +2,7 @@
|
||||
namespace App\Repositories;
|
||||
|
||||
use App\Enums\ModelEventEnum;
|
||||
use App\Enums\RedisKeysEnum;
|
||||
use App\Exceptions\InsufficientPermissionException;
|
||||
use App\Exceptions\NexusException;
|
||||
use App\Http\Resources\ExamUserResource;
|
||||
@@ -252,9 +253,15 @@ class UserRepository extends BaseRepository
|
||||
do_log("user: $uid, $modCommentText, update: " . nexus_json_encode($update));
|
||||
$this->clearCache($targetUser);
|
||||
fire_event("user_enabled", $targetUser);
|
||||
$this->setEnableLatelyCache($targetUser->id);
|
||||
return true;
|
||||
}
|
||||
|
||||
private function setEnableLatelyCache(int $userId): void
|
||||
{
|
||||
NexusDB::cache_put(User::getUserEnableLatelyCacheKey($userId), now()->toDateTimeString());
|
||||
}
|
||||
|
||||
public function getInviteInfo($id)
|
||||
{
|
||||
$user = User::query()->findOrFail($id, ['id']);
|
||||
|
||||
@@ -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::create('require_seed_torrents', function (Blueprint $table) {
|
||||
$table->id();
|
||||
$table->integer('torrent_id')->unique();
|
||||
$table->timestamps();
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Reverse the migrations.
|
||||
*/
|
||||
public function down(): void
|
||||
{
|
||||
Schema::dropIfExists('require_seed_torrents');
|
||||
}
|
||||
};
|
||||
@@ -0,0 +1,33 @@
|
||||
<?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::create('user_require_seed_torrents', function (Blueprint $table) {
|
||||
$table->id();
|
||||
$table->integer('user_id');
|
||||
$table->integer('torrent_id')->index();
|
||||
$table->bigInteger('seed_time_begin');
|
||||
$table->bigInteger('uploaded_begin');
|
||||
$table->dateTime('last_settlement_at')->nullable()->index();
|
||||
$table->timestamps();
|
||||
$table->unique(['user_id', 'torrent_id']);
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Reverse the migrations.
|
||||
*/
|
||||
public function down(): void
|
||||
{
|
||||
Schema::dropIfExists('user_require_seed_torrents');
|
||||
}
|
||||
};
|
||||
@@ -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('snatched', function (Blueprint $table) {
|
||||
$table->bigInteger('leech_time_no_seeder')->default(0);
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Reverse the migrations.
|
||||
*/
|
||||
public function down(): void
|
||||
{
|
||||
Schema::table('snatched', function (Blueprint $table) {
|
||||
$table->dropColumn('leech_time_no_seeder');
|
||||
});
|
||||
}
|
||||
};
|
||||
@@ -12,7 +12,7 @@ return new class extends Migration
|
||||
public function up(): void
|
||||
{
|
||||
Schema::table('hit_and_runs', function (Blueprint $table) {
|
||||
$table->bigInteger('leech_time_no_seeder')->default(0);
|
||||
$table->bigInteger('leech_time_no_seeder_begin')->default(0);
|
||||
});
|
||||
}
|
||||
|
||||
@@ -22,7 +22,7 @@ return new class extends Migration
|
||||
public function down(): void
|
||||
{
|
||||
Schema::table('hit_and_runs', function (Blueprint $table) {
|
||||
$table->dropColumn('leech_time_no_seeder');
|
||||
$table->dropColumn('leech_time_no_seeder_begin');
|
||||
});
|
||||
}
|
||||
};
|
||||
@@ -0,0 +1,29 @@
|
||||
<?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('torrents', function (Blueprint $table) {
|
||||
$table->index('added');
|
||||
$table->index(['promotion_until', 'promotion_time_type']);
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Reverse the migrations.
|
||||
*/
|
||||
public function down(): void
|
||||
{
|
||||
Schema::table('torrents', function (Blueprint $table) {
|
||||
//
|
||||
});
|
||||
}
|
||||
};
|
||||
@@ -237,6 +237,11 @@ function disable_user(\Illuminate\Database\Eloquent\Builder $query, $reasonKey)
|
||||
$userModifyLogs = [];
|
||||
foreach ($results as $user) {
|
||||
$uid = $user->id;
|
||||
$enableCacheResult = \Nexus\Database\NexusDB::cache_get(\App\Models\User::getUserEnableLatelyCacheKey($uid));
|
||||
if ($enableCacheResult) {
|
||||
do_log(sprintf("user: %s just enable at: %s, skip", $uid, $enableCacheResult));
|
||||
continue;
|
||||
}
|
||||
$uidArr[] = $uid;
|
||||
$reason = nexus_trans($reasonKey, [], $user->locale);
|
||||
$userBanLogData[] = [
|
||||
@@ -251,6 +256,9 @@ function disable_user(\Illuminate\Database\Eloquent\Builder $query, $reasonKey)
|
||||
'updated_at' => date("Y-m-d H:i:s"),
|
||||
];
|
||||
}
|
||||
if (empty($uidArr)) {
|
||||
return [];
|
||||
}
|
||||
$sql = sprintf(
|
||||
"update users set enabled = '%s' where id in (%s)",
|
||||
\App\Models\User::ENABLED_NO, implode(', ', $uidArr)
|
||||
@@ -275,9 +283,7 @@ function docleanup($forceAll = 0, $printProgress = false) {
|
||||
global $Cache;
|
||||
global $rootpath;
|
||||
$requestId = nexus()->getRequestId();
|
||||
|
||||
// require_once($rootpath . '/lang/_target/lang_cleanup.php');
|
||||
|
||||
set_time_limit(0);
|
||||
ignore_user_abort(1);
|
||||
$now = time();
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
<?php
|
||||
defined('VERSION_NUMBER') || define('VERSION_NUMBER', '1.9.7');
|
||||
defined('RELEASE_DATE') || define('RELEASE_DATE', '2025-07-21');
|
||||
defined('RELEASE_DATE') || define('RELEASE_DATE', '2025-09-08');
|
||||
defined('IN_TRACKER') || define('IN_TRACKER', false);
|
||||
defined('PROJECTNAME') || define("PROJECTNAME","NexusPHP");
|
||||
defined('NEXUSPHPURL') || define("NEXUSPHPURL","https://nexusphp.org");
|
||||
|
||||
@@ -2810,7 +2810,7 @@ if ($msgalert)
|
||||
$Cache->cache_value($cacheKey, $toApprovalCounts, 60);
|
||||
}
|
||||
if ($toApprovalCounts) {
|
||||
msgalert('torrents.php?approval_status=0', sprintf($lang_functions['text_torrent_to_approval'], is_or_are($toApprovalCounts), $toApprovalCounts, add_s($toApprovalCounts)), 'darkred');
|
||||
msgalert('torrents.php?approval_status=0&incldead=0', sprintf($lang_functions['text_torrent_to_approval'], is_or_are($toApprovalCounts), $toApprovalCounts, add_s($toApprovalCounts)), 'darkred');
|
||||
}
|
||||
}
|
||||
|
||||
@@ -3007,17 +3007,7 @@ function get_protocol_prefix()
|
||||
if (isHttps()) {
|
||||
return "https://";
|
||||
}
|
||||
if ($securelogin == "yes") {
|
||||
return "https://";
|
||||
} elseif ($securelogin == "no") {
|
||||
return "http://";
|
||||
} else {
|
||||
if (!isset($_COOKIE["c_secure_ssl"])) {
|
||||
return "http://";
|
||||
} else {
|
||||
return base64_decode($_COOKIE["c_secure_ssl"]) == "yeah" ? "https://" : "http://";
|
||||
}
|
||||
}
|
||||
return 'http://';
|
||||
}
|
||||
|
||||
function get_langid_from_langcookie($lang = '')
|
||||
@@ -5818,7 +5808,7 @@ function can_access_torrent($torrent, $uid)
|
||||
|
||||
function get_ip_location_from_geoip($ip): bool|array
|
||||
{
|
||||
$locationInfo = \Nexus\Database\NexusDB::remember("locations_{$ip}", 3600, function () use ($ip) {
|
||||
$locationInfo = \Nexus\Database\NexusDB::remember("locations_{$ip}", 864000, function () use ($ip) {
|
||||
$lang = get_langfolder_cookie();
|
||||
$langMap = [
|
||||
'chs' => 'zh-CN',
|
||||
@@ -5861,7 +5851,7 @@ function get_ip_location_from_geoip($ip): bool|array
|
||||
$info['continent'] = $continentName;
|
||||
$info['continent_en'] = $record->continent->names['en'] ?? '';
|
||||
} catch (\Exception $exception) {
|
||||
do_log($exception->getMessage() . $exception->getTraceAsString(), 'error');
|
||||
do_log($exception->getMessage());
|
||||
}
|
||||
return $info;
|
||||
});
|
||||
@@ -5898,7 +5888,7 @@ function msgalert($url, $text, $bgcolor = "red")
|
||||
function build_medal_image(\Illuminate\Support\Collection $medals, $maxHeight = 200, $withActions = false): string
|
||||
{
|
||||
$medalImages = [];
|
||||
$wrapBefore = '<form><div style="display: flex;justify-content: center;margin-top: 10px;">';
|
||||
$wrapBefore = '<form><div style="display: flex;flex-wrap: wrap;justify-content: center;margin-top: 10px;">';
|
||||
$wrapAfter = '</div></form>';
|
||||
foreach ($medals as $medal) {
|
||||
$html = sprintf('<div style="display: flex;flex-direction: column;justify-content: space-between;margin-right: 10px"><div><img src="%s" title="%s" class="preview" style="max-height: %spx;max-width: %spx"/></div>', $medal->image_large, $medal->name, $maxHeight, $maxHeight);
|
||||
@@ -6003,6 +5993,7 @@ function calculate_seed_bonus($uid, $torrentIdArr = null): array
|
||||
$nzero_bonus = $settingBonus['nzero'];
|
||||
$bzero_bonus = $settingBonus['bzero'];
|
||||
$l_bonus = $settingBonus['l'];
|
||||
$minSize = $settingBonus['min_size'] ?? 0;
|
||||
|
||||
$sqrtof2 = sqrt(2);
|
||||
$logofpointone = log(0.1);
|
||||
@@ -6022,9 +6013,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)";
|
||||
$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";
|
||||
} 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' group by peers.torrent, peers.peer_id";
|
||||
$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";
|
||||
}
|
||||
$tagGrouped = [];
|
||||
$torrentResult = \Nexus\Database\NexusDB::select($sql);
|
||||
|
||||
@@ -763,7 +763,13 @@ function get_user_row($id)
|
||||
if (isset($userRows[$id])) return $userRows[$id];
|
||||
$cacheKey = 'user_'.$id.'_content';
|
||||
$row = \Nexus\Database\NexusDB::remember($cacheKey, 3600, function () use ($id, $neededColumns) {
|
||||
$user = \App\Models\User::query()->with(['wearing_medals'])->find($id, $neededColumns);
|
||||
$user = \App\Models\User::query()->with([
|
||||
'wearing_medals' => function ($query) {
|
||||
$query->orderBy('user_medals.priority', 'desc')
|
||||
->orderBy('user_medals.id', 'desc')
|
||||
->limit(get_setting('system.maximum_number_of_medals_can_be_worn', 3));
|
||||
}
|
||||
])->find($id, $neededColumns);
|
||||
if (!$user) {
|
||||
return null;
|
||||
}
|
||||
@@ -1392,7 +1398,7 @@ function fire_event(string $name, \Illuminate\Database\Eloquent\Model $model, ?\
|
||||
}
|
||||
}
|
||||
call_user_func_array([$eventClass, "dispatch"], $params);
|
||||
publish_model_event($name, $model->id);
|
||||
publish_model_event($name, $model->id, $model->toJson());
|
||||
do_log("success fire_event in laravel, name: $name, id: $model->id, oldId: " . ($oldModel ? $oldModel->id : ""));
|
||||
}
|
||||
}
|
||||
@@ -1400,11 +1406,11 @@ function fire_event(string $name, \Illuminate\Database\Eloquent\Model $model, ?\
|
||||
/**
|
||||
* 仅仅是往 redis 发布事件, php 端无监听者仅在其他平台有需要的触发这个即可, 较轻量
|
||||
*/
|
||||
function publish_model_event(string $event, int $id): void
|
||||
function publish_model_event(string $event, int $id, string $json = ""): void
|
||||
{
|
||||
$channel = nexus_env("CHANNEL_NAME_MODEL_EVENT");
|
||||
if (!empty($channel)) {
|
||||
\Nexus\Database\NexusDB::redis()->publish($channel, json_encode(["event" => $event, "id" => $id]));
|
||||
\Nexus\Database\NexusDB::redis()->publish($channel, json_encode(["event" => $event, "id" => $id, "json" => $json]));
|
||||
} else {
|
||||
do_log("event: $event, id: $id, channel: $channel, channel is empty!", "error");
|
||||
}
|
||||
|
||||
@@ -818,6 +818,9 @@ $lang_settings = array
|
||||
'row_complain_enabled_note' => '默认: "yes"',
|
||||
'row_record_announce_logs' => '记录汇报日志',
|
||||
'text_record_announce_logs_note' => '要启用,请先安装并启动 ClickHouse,并在 .env 文件中添加配置',
|
||||
'row_min_size' => '起步体积',
|
||||
'text_bonus_mini_size' => '要求种子的体积最小为',
|
||||
'text_bonus_mini_size_help' => '。小于此体积的种子不参与魔力计算。单位:字节(Byte),如 1 Byte = 1024 KiB。',
|
||||
);
|
||||
|
||||
?>
|
||||
|
||||
@@ -816,6 +816,9 @@ $lang_settings = array
|
||||
'text_use_challenge_response_authentication_note' => '如果啟用,登錄時將不傳輸明文密碼,建議啟用。未來版本會刪除此配置且啟用此功能。',
|
||||
'row_complain_enabled' => '啟用申訴',
|
||||
'row_complain_enabled_note' => '默認: "yes"',
|
||||
'row_min_size' => '起步體積',
|
||||
'text_bonus_mini_size' => '要求種子的體積最小爲',
|
||||
'text_bonus_mini_size_help' => '。小於此體積的種子不參與魔力計算。單位:字節(Byte),如 1 Byte = 1024 KiB。',
|
||||
);
|
||||
|
||||
?>
|
||||
|
||||
@@ -816,6 +816,9 @@ $lang_settings = array
|
||||
'text_use_challenge_response_authentication_note' => 'If enabled, no plaintext passwords will be transmitted at login, recommended. Future releases will remove this configuration and enable this feature.' ,
|
||||
'row_complain_enabled' => 'Whether to enable complaints',
|
||||
'row_complain_enabled_note' => 'default: "yes"',
|
||||
'row_min_size' => 'Minimum volume',
|
||||
'text_bonus_mini_size'=> 'The minimum volume required for torrent is',
|
||||
'text_bonus_mini_size_help' => '. Torrent size smaller than this volume are not included in the bonus calculation. Unit: bytes (Byte), e.g., 1 Byte = 1024 KiB.',
|
||||
);
|
||||
|
||||
?>
|
||||
|
||||
@@ -47,17 +47,16 @@ class Imdb
|
||||
|
||||
private function checkDir($dir, $langKeyPrefix)
|
||||
{
|
||||
global $lang_functions;
|
||||
if (!is_dir($dir)) {
|
||||
$mkdirResult = mkdir($dir, 0777, true);
|
||||
if ($mkdirResult !== true) {
|
||||
$msg = $lang_functions["{$langKeyPrefix}_can_not_create"];
|
||||
$msg = nexus_trans("torrent.{$langKeyPrefix}_can_not_create");
|
||||
do_log("$msg, dir: $dir");
|
||||
throw new ImdbException($msg);
|
||||
}
|
||||
}
|
||||
if (!is_writable($dir)) {
|
||||
$msg = $lang_functions["{$langKeyPrefix}_is_not_writeable"];
|
||||
$msg = nexus_trans("torrent.{$langKeyPrefix}_is_not_writeable");
|
||||
do_log("$msg, dir: $dir");
|
||||
throw new ImdbException($msg);
|
||||
}
|
||||
|
||||
@@ -376,6 +376,7 @@ class Update extends Install
|
||||
"enabled" => 1,
|
||||
"is_default" => 1,
|
||||
]);
|
||||
TrackerUrl::saveUrlCache();
|
||||
NexusDB::cache_del("nexus_plugin_store_all");
|
||||
}
|
||||
/**
|
||||
|
||||
@@ -401,6 +401,8 @@ if (
|
||||
}
|
||||
}
|
||||
|
||||
$leechTimeNoSeeder = "";
|
||||
|
||||
// current peer_id, or you could say session with tracker not found in table peers
|
||||
if (!isset($self))
|
||||
{
|
||||
@@ -473,13 +475,18 @@ else // continue an existing session
|
||||
}
|
||||
|
||||
do_log("upthis: $upthis, downthis: $downthis, announcetime: $announcetime, is_cheater: $is_cheater");
|
||||
$snatchInfo = get_snatch_info($torrentid, $userid);
|
||||
if (!isset($snatchInfo)) {
|
||||
$snatchInfo = get_snatch_info($torrentid, $userid);
|
||||
}
|
||||
if (!$is_cheater && ($trueupthis > 0 || $truedownthis > 0))
|
||||
{
|
||||
$dataTraffic = getDataTraffic($torrent, $_GET, $az, $self, $snatchInfo, apply_filter('torrent_promotion', $torrent));
|
||||
$USERUPDATESET[] = "uploaded = uploaded + " . $dataTraffic['uploaded_increment_for_user'];
|
||||
$USERUPDATESET[] = "downloaded = downloaded + " . $dataTraffic['downloaded_increment_for_user'];
|
||||
}
|
||||
if ($torrent['seeders'] <= 0 && $seeder == 'no' && $self['announcetime'] > 0) {
|
||||
$leechTimeNoSeeder = ", leech_time_no_seeder = leech_time_no_seeder + {$self['announcetime']}";
|
||||
}
|
||||
}
|
||||
|
||||
$dt = sqlesc(date("Y-m-d H:i:s"));
|
||||
@@ -495,7 +502,7 @@ if (isset($self) && $event == "stopped")
|
||||
{
|
||||
$updateset[] = ($self["seeder"] == "yes" ? "seeders = seeders - 1" : "leechers = leechers - 1");
|
||||
$hasChangeSeederLeecher = true;
|
||||
sql_query("UPDATE snatched SET uploaded = uploaded + $trueupthis, downloaded = downloaded + $truedownthis, to_go = $left, $announcetime, last_action = ".$dt." WHERE id = {$snatchInfo['id']}") or err("SL Err 1");
|
||||
sql_query("UPDATE snatched SET uploaded = uploaded + $trueupthis, downloaded = downloaded + $truedownthis, to_go = $left, $announcetime $leechTimeNoSeeder, last_action = ".$dt." WHERE id = {$snatchInfo['id']}") or err("SL Err 1");
|
||||
}
|
||||
}
|
||||
elseif(isset($self))
|
||||
@@ -518,7 +525,7 @@ elseif(isset($self))
|
||||
$hasChangeSeederLeecher = true;
|
||||
}
|
||||
if (!empty($snatchInfo)) {
|
||||
sql_query("UPDATE snatched SET uploaded = uploaded + $trueupthis, downloaded = downloaded + $truedownthis, to_go = $left, $announcetime, last_action = ".$dt." $finished_snatched WHERE id = {$snatchInfo['id']}") or err("SL Err 2");
|
||||
sql_query("UPDATE snatched SET uploaded = uploaded + $trueupthis, downloaded = downloaded + $truedownthis, to_go = $left, $announcetime, last_action = ".$dt." $finished_snatched $leechTimeNoSeeder WHERE id = {$snatchInfo['id']}") or err("SL Err 2");
|
||||
do_action('snatched_saved', $torrent, $snatchInfo);
|
||||
}
|
||||
}
|
||||
@@ -571,14 +578,16 @@ if (($left > 0 || $event == "completed") && $az['class'] < \App\Models\HitAndRun
|
||||
$hrLog .= ", hrExists: $hrExists";
|
||||
if (!$hrExists) {
|
||||
//last check include rate
|
||||
$includeRate = \App\Models\HitAndRun::getConfig('include_rate', $torrent['mode']);
|
||||
if ($includeRate === "" || $includeRate === null) {
|
||||
//not set yet
|
||||
$includeRate = 1;
|
||||
}
|
||||
$includeRate = (float)\App\Models\HitAndRun::getConfig('include_rate', $torrent['mode']);
|
||||
// if ($includeRate === "" || $includeRate === null) {
|
||||
// //not set yet
|
||||
// $includeRate = 1;
|
||||
// }
|
||||
$hrLog .= ", includeRate: $includeRate";
|
||||
//get newest snatch info
|
||||
$snatchInfo = get_snatch_info($torrentid, $userid);
|
||||
if (!isset($snatchInfo)) {
|
||||
$snatchInfo = get_snatch_info($torrentid, $userid);
|
||||
}
|
||||
$requiredDownloaded = $torrent['size'] * $includeRate;
|
||||
if ($snatchInfo['downloaded'] >= $requiredDownloaded) {
|
||||
$nowStr = date('Y-m-d H:i:s');
|
||||
@@ -598,28 +607,13 @@ if (($left > 0 || $event == "completed") && $az['class'] < \App\Models\HitAndRun
|
||||
do_log("$hrLog, total downloaded: {$snatchInfo['downloaded']} < required: $requiredDownloaded", "debug");
|
||||
}
|
||||
} else {
|
||||
$hrLog .= ", already exists";
|
||||
if (isset($self) && $torrent['seeders'] <= 0) {
|
||||
$hrLeechTimeMin = \App\Models\HitAndRun::getConfig('leech_time_minimum', $torrent['mode']);
|
||||
if ($hrLeechTimeMin > 0) {
|
||||
$hrLog .= ", enable hrLeechTimeMin: $hrLeechTimeMin";
|
||||
$hrInfo = json_decode($hrExists, true);
|
||||
if ($hrInfo['status'] == \App\Models\HitAndRun::STATUS_INSPECTING) {
|
||||
sql_query("update hit_and_runs set leech_time_no_seeder = leech_time_no_seeder + {$self['announcetime']} where id = {$hrInfo['id']} limit 1");
|
||||
} else {
|
||||
do_log("$hrLog, hr status != STATUS_INSPECTING", "debug");
|
||||
}
|
||||
} else {
|
||||
do_log("$hrLog, not enable hrLeechTimeMin", "debug");
|
||||
}
|
||||
} else {
|
||||
do_log("$hrLog, no self or seeders({$torrent['seeders']}) > 0", "debug");
|
||||
}
|
||||
do_log("$hrLog, already exists", 'debug');
|
||||
}
|
||||
} else {
|
||||
do_log("$hrLog, not match", "debug");
|
||||
}
|
||||
}
|
||||
|
||||
// revert to only increment/decrement
|
||||
//if (isset($event) && !empty($event)) {
|
||||
// $updateset[] = 'seeders = ' . get_row_count("peers", "where torrent = $torrentid and to_go = 0");
|
||||
@@ -660,6 +654,12 @@ $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);
|
||||
}
|
||||
if (\App\Repositories\RequireSeedTorrentRepository::shouldRecordUser($redis, $userid, $torrentid)) {
|
||||
if (!isset($snatchInfo)) {
|
||||
$snatchInfo = get_snatch_info($torrentid, $userid);
|
||||
}
|
||||
\App\Repositories\RequireSeedTorrentRepository::recordUser($redis, $userid, $torrentid, $snatchInfo);
|
||||
}
|
||||
do_action('announced', $torrent, $az, $_REQUEST);
|
||||
benc_resp($rep_dict);
|
||||
?>
|
||||
|
||||
@@ -39,7 +39,7 @@ if ($showaudiocodec) $audiocodecs = searchbox_item_list("audiocodecs", $brsectio
|
||||
}
|
||||
stdhead($lang_getrss['head_rss_feeds']);
|
||||
$query = [];
|
||||
$allowed_showrows=array('10','50','100','200');
|
||||
$allowed_showrows=array('10','50');
|
||||
$stickyTypes = [
|
||||
0 => nexus_trans('torrent.pos_state_normal'),
|
||||
1 => nexus_trans('torrent.pos_state_sticky'),
|
||||
@@ -365,17 +365,6 @@ if (get_setting('main.spsct') == 'yes') {
|
||||
}
|
||||
?>
|
||||
</select></td></tr>
|
||||
<tr><td class="rowhead"><?php echo $lang_getrss['row_keyword']?></td>
|
||||
<td class="rowfollow" align="left">
|
||||
<input type="text" name="search" style="width: 200px;" /> <?php echo $lang_getrss['text_with']?>
|
||||
<select name="search_mode" style="width: 60px;">
|
||||
<option value="0"><?php echo $lang_getrss['select_and'] ?></option>
|
||||
<option value="2"><?php echo $lang_getrss['select_exact'] ?></option>
|
||||
</select>
|
||||
<?php echo $lang_getrss['text_mode']?>
|
||||
<div><?php echo $lang_getrss['text_keyword_note'] ?></div>
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td colspan="2" align="center">
|
||||
<input type="submit" value="<?php echo $lang_getrss['submit_generatte_rss_link']?>" />
|
||||
|
||||
@@ -111,7 +111,9 @@ if ($action == "edituser")
|
||||
if ($arr['email'] != $email){
|
||||
$updateset[] = "email = " . sqlesc($email);
|
||||
// $modcomment = date("Y-m-d") . " - Email changed from $arr[email] to $email by {$CURUSER['username']}.\n". $modcomment;
|
||||
$userModifyLogs[] = "Email changed from $arr[email] to $email by {$CURUSER['username']}.";
|
||||
$modifyLog = "Email changed from $arr[email] to $email by {$CURUSER['username']}.";
|
||||
do_log($modifyLog, "alert");
|
||||
$userModifyLogs[] = $modifyLog;
|
||||
$locale = get_user_locale($userid);
|
||||
$subject = sqlesc(nexus_trans("user.msg_email_change", [], $locale));
|
||||
$msg = sqlesc(nexus_trans("user.msg_your_email_changed_from", [], $locale).$arr['email'].nexus_trans("user.msg_to_new", [], $locale) . $email .nexus_trans("user.msg_by", [], $locale).$CURUSER['username']);
|
||||
|
||||
@@ -51,6 +51,7 @@ if ($_SERVER["REQUEST_METHOD"] == "POST")
|
||||
$title = $SITENAME.$lang_recover['mail_title'];
|
||||
$mailOne = sprintf($lang_recover['mail_one'], $siteName);
|
||||
$mailFour = sprintf($lang_recover['mail_four'], $siteName);
|
||||
\Nexus\Database\NexusDB::cache_put("recover:$hash", now()->toDateTimeString());
|
||||
|
||||
$body = <<<EOD
|
||||
{$mailOne}($email){$lang_recover['mail_two']}$ip{$lang_recover['mail_three']}
|
||||
@@ -69,7 +70,10 @@ elseif($_SERVER["REQUEST_METHOD"] == "GET" && $take_recover && isset($_GET["id"]
|
||||
$md5 = $_GET["secret"];
|
||||
if (!$id)
|
||||
httperr();
|
||||
|
||||
if (!\Nexus\Database\NexusDB::cache_get("recover:$md5")) {
|
||||
do_log("secret: $md5 is expired", "error");
|
||||
httperr();
|
||||
}
|
||||
$res = sql_query("SELECT username, email, passhash, editsecret FROM users WHERE id = " . sqlesc($id)) or sqlerr(__FILE__, __LINE__);
|
||||
$arr = mysql_fetch_array($res) or httperr();
|
||||
|
||||
|
||||
@@ -99,7 +99,7 @@ elseif ($action == 'savesettings_bonus') // save bonus
|
||||
'tengbupload', 'ratiolimit','dlamountlimit','oneinvite','customtitle','vipstatus','bonusgift', 'basictax', 'taxpercentage',
|
||||
'prolinkpoint', 'prolinktime', 'attendance_initial', 'attendance_step', 'attendance_max', 'cancel_hr', 'attendance_card',
|
||||
'harem_addition', 'hundredgbupload', 'tengbdownload', 'hundredgbdownload', 'official_addition', 'official_tag', 'zero_bonus_tag', 'zero_bonus_factor',
|
||||
'one_tmp_invite', 'rainbow_id', 'change_username_card',
|
||||
'one_tmp_invite', 'rainbow_id', 'change_username_card', 'min_size'
|
||||
);
|
||||
GetVar($validConfig);
|
||||
$BONUS = [];
|
||||
@@ -576,6 +576,7 @@ elseif ($action == 'bonussettings'){
|
||||
print ($notice);
|
||||
print ("<form method='post' action='".$_SERVER["SCRIPT_NAME"]."'><input type='hidden' name='action' value='savesettings_bonus'>");
|
||||
print("<tr><td colspan=2 align=center><b>".$lang_settings['text_bonus_by_seeding']."</b></td></tr>");
|
||||
tr($lang_settings['row_min_size'], $lang_settings['text_bonus_mini_size']."<input type='text' style=\"width: 100px\" name=min_size value='".(isset($BONUS["min_size"]) ? $BONUS["min_size"] : 0 )."'>".$lang_settings['text_bonus_mini_size_help'],1);
|
||||
tr($lang_settings['row_donor_gets_double'], $lang_settings['text_donor_gets']."<input type='text' style=\"width: 50px\" name=donortimes value='".(isset($BONUS["donortimes"]) ? $BONUS["donortimes"] : 2 )."'>".$lang_settings['text_times_as_many'],1);
|
||||
tr($lang_settings['row_basic_seeding_bonus'], $lang_settings['text_user_would_get']."<input type='text' style=\"width: 50px\" name=perseeding value='".(isset($BONUS["perseeding"]) ? $BONUS["perseeding"] : 1 )."'>".$lang_settings['text_bonus_points']."<input type='text' style=\"width: 50px\" name=maxseeding value='".(isset($BONUS["maxseeding"]) ? $BONUS["maxseeding"] : 7 )."'>".$lang_settings['text_torrents_default'], 1);
|
||||
|
||||
|
||||
@@ -15,9 +15,10 @@ $id = intval($_GET['id'] ?? 0);
|
||||
int_check($id,true);
|
||||
|
||||
if (get_user_class() >= UC_MODERATOR || $CURUSER['id'] == "$id")
|
||||
{
|
||||
{
|
||||
$deadtime = deadtime();
|
||||
sql_query("DELETE FROM peers WHERE last_action < FROM_UNIXTIME($deadtime) AND userid=" . sqlesc($id));
|
||||
$lastAction = date("Y-m-d H:i:s", $deadtime);
|
||||
sql_query("DELETE FROM peers WHERE last_action < '$lastAction' AND userid=" . sqlesc($id));
|
||||
$effected = mysql_affected_rows();
|
||||
|
||||
stderr($lang_takeflush['std_success'], "$effected ".$lang_takeflush['std_ghost_torrents_cleaned']);
|
||||
|
||||
@@ -93,8 +93,10 @@ $torrent = unesc($_POST["name"]);
|
||||
if ($f['size'] > $max_torrent_size)
|
||||
bark($lang_takeupload['std_torrent_file_too_big'].number_format($max_torrent_size).$lang_takeupload['std_remake_torrent_note']);
|
||||
$tmpname = $f["tmp_name"];
|
||||
if (!is_uploaded_file($tmpname))
|
||||
bark("eek");
|
||||
if (!is_uploaded_file($tmpname)) {
|
||||
do_log("eek, FILE: " . nexus_json_encode($f), 'error');
|
||||
bark("eek");
|
||||
}
|
||||
if (!filesize($tmpname))
|
||||
bark($lang_takeupload['std_empty_file']);
|
||||
|
||||
|
||||
@@ -37,7 +37,8 @@ if ($passkey){
|
||||
}
|
||||
}
|
||||
}
|
||||
$searchstr = mysql_real_escape_string(trim($_GET["search"] ?? ''));
|
||||
//$searchstr = mysql_real_escape_string(trim($_GET["search"] ?? ''));
|
||||
$searchstr = null;//don't support search, use client self filter instead
|
||||
if (empty($searchstr))
|
||||
unset($searchstr);
|
||||
if (isset($searchstr)){
|
||||
@@ -81,8 +82,8 @@ if ($startindex) {
|
||||
$limit .= $startindex.", ";
|
||||
}
|
||||
$showrows = intval($_GET['rows'] ?? 0);
|
||||
if($showrows < 1 || $showrows > 200) {
|
||||
$showrows = 10;
|
||||
if($showrows < 1 || $showrows > 50) {
|
||||
$showrows = 50;
|
||||
}
|
||||
$limit .= $showrows;
|
||||
|
||||
|
||||
@@ -738,7 +738,7 @@ if (isset($searchstr))
|
||||
{
|
||||
$searchstr_element = trim($searchstr_element); // furthur trim to ensure that multi space seperated words still work
|
||||
$searchstr_exploded_count++;
|
||||
if ($searchstr_exploded_count > 10) // maximum 10 keywords
|
||||
if ($searchstr_exploded_count > 3) // maximum 3 keywords
|
||||
break;
|
||||
$like_expression_array[] = " LIKE '%" . $searchstr_element. "%'";
|
||||
}
|
||||
|
||||
@@ -27,6 +27,11 @@ return [
|
||||
\App\Models\BonusLogs::BUSINESS_TYPE_RECEIVE_REWARD => 'Receive reward',
|
||||
\App\Models\BonusLogs::BUSINESS_TYPE_RECEIVE_GIFT => 'Receive gift',
|
||||
\App\Models\BonusLogs::BUSINESS_TYPE_UPLOAD_TORRENT => 'Upload torrent',
|
||||
\App\Models\BonusLogs::BUSINESS_TYPE_SEEDING_BASIC => 'Seeding basic',
|
||||
\App\Models\BonusLogs::BUSINESS_TYPE_SEEDING_DONOR_ADDITION => 'Seeding donor addition',
|
||||
\App\Models\BonusLogs::BUSINESS_TYPE_SEEDING_OFFICIAL_ADDITION => 'Seeding official addition',
|
||||
\App\Models\BonusLogs::BUSINESS_TYPE_SEEDING_HAREM_ADDITION => 'Seeding harem addition',
|
||||
\App\Models\BonusLogs::BUSINESS_TYPE_SEEDING_MEDAL_ADDITION => 'Seeding medal addition',
|
||||
],
|
||||
'fields' => [
|
||||
'business_type' => 'Business type',
|
||||
|
||||
@@ -107,4 +107,9 @@ return [
|
||||
'msg_here' => " [b]here[/b]",
|
||||
'msg_offer' => "Offer ",
|
||||
'msg_blank' => ".",
|
||||
'require_seed_section_menu_title' => 'Require Seed',
|
||||
'imdb_cache_dir_can_not_create' => 'imdb cache dir can not create',
|
||||
'imdb_cache_dir_is_not_writeable' => 'imdb cache dir is not writeable',
|
||||
'imdb_photo_dir_can_not_create' => 'imdb photo dir can not create',
|
||||
'imdb_photo_dir_is_not_writeable' => 'imdb photo dir is not writeable',
|
||||
];
|
||||
|
||||
@@ -29,6 +29,11 @@ return [
|
||||
\App\Models\BonusLogs::BUSINESS_TYPE_RECEIVE_REWARD => '收到奖励',
|
||||
\App\Models\BonusLogs::BUSINESS_TYPE_RECEIVE_GIFT => '收到礼物',
|
||||
\App\Models\BonusLogs::BUSINESS_TYPE_UPLOAD_TORRENT => '发布种子',
|
||||
\App\Models\BonusLogs::BUSINESS_TYPE_SEEDING_BASIC => '做种基础魔力',
|
||||
\App\Models\BonusLogs::BUSINESS_TYPE_SEEDING_DONOR_ADDITION => '做种捐赠加成',
|
||||
\App\Models\BonusLogs::BUSINESS_TYPE_SEEDING_OFFICIAL_ADDITION => '做种官种加成',
|
||||
\App\Models\BonusLogs::BUSINESS_TYPE_SEEDING_HAREM_ADDITION => '做种后宫加成',
|
||||
\App\Models\BonusLogs::BUSINESS_TYPE_SEEDING_MEDAL_ADDITION => '做种勋章加成',
|
||||
],
|
||||
'fields' => [
|
||||
'business_type' => '业务类型',
|
||||
|
||||
@@ -91,6 +91,47 @@ return [
|
||||
'include_rate' => '计入完成率',
|
||||
'include_rate_help' => '当下载完成率(0 ~ 1 之间的小数)达到此值时才计入 H&R。默认:1'
|
||||
],
|
||||
'require_seed_section' => [
|
||||
'tab_header' => '急需保种列表',
|
||||
'menu_title' => '菜单名称',
|
||||
'enabled_help' => '注意:此列表的种子不受盒子规则限制',
|
||||
'menu_title_help' => '若留空,使用默认值:急需保种',
|
||||
'seeder_lte' => '做种数小于等于',
|
||||
'seeder_lte_help' => '满足此条件的种子才进入此列表。默认 0',
|
||||
'seeder_gte' => '做种数大于等于',
|
||||
'seeder_gte_help' => '满足此条件的种子才进入此列表。默认 0',
|
||||
'promotion_state' => '促销状态',
|
||||
'promotion_state_help' => '移出列表后有效性保留24小时。默认 Free',
|
||||
'bonus_reward' => '魔力奖励',
|
||||
'bonus_reward_help' => '按做种数由小到大配置。窗口都是采用去尾法,即不足一个窗口的舍弃掉。时间奖励窗口单位是小时,上传量窗口单位是GiB',
|
||||
'require_tags' => '包含标签',
|
||||
'require_tags_help' => '至少包含选中标签中的一个才进入此列表。留空则不限制。',
|
||||
'seeders' => '做种数小于等于',
|
||||
'daily_seed_time_min' => '每天最小做种时间',
|
||||
'daily_seed_time_min_help' => '单位:小时。每天 0 点结算,统计前一天 24 小时内做种时间,不小于此小时数的种子才有奖励',
|
||||
'torrent_count_max' => '数量上限',
|
||||
'torrent_count_max_help' => '列表允许的最大的种子数量。必须严格控制数量,过多失则去了意义。默认:100',
|
||||
|
||||
'seed_time_reward' => '做种时间奖励',
|
||||
'seed_time_reward_begin' => '开始值',
|
||||
'seed_time_reward_begin_help' => '',
|
||||
'seed_time_reward_end' => '结束值',
|
||||
'seed_time_reward_end_help' => '',
|
||||
'seed_time_reward_window' => '窗口',
|
||||
'seed_time_reward_window_help' => '',
|
||||
'seed_time_reward_reward' => '奖励',
|
||||
'seed_time_reward_reward_help' => '',
|
||||
|
||||
'data_traffic_reward' => '上传量奖励',
|
||||
'data_traffic_reward_begin' => '开始值',
|
||||
'data_traffic_reward_begin_help' => '',
|
||||
'data_traffic_reward_end' => '结束值',
|
||||
'data_traffic_reward_end_help' => '',
|
||||
'data_traffic_reward_window' => '窗口',
|
||||
'data_traffic_reward_window_help' => '',
|
||||
'data_traffic_reward_reward' => '奖励',
|
||||
'data_traffic_reward_reward_help' => '',
|
||||
],
|
||||
'seed_box' => [
|
||||
'tab_header' => 'SeedBox',
|
||||
'enabled_help' => '是否启用 SeedBox 规则',
|
||||
|
||||
@@ -106,4 +106,9 @@ return [
|
||||
'msg_here' => "[b]这里[/b]",
|
||||
'msg_offer' => "候选 ",
|
||||
'msg_blank' => "刪除。",
|
||||
'require_seed_section_menu_title' => '急需保种',
|
||||
'imdb_cache_dir_can_not_create' => 'imdb 缓存目录无法创建',
|
||||
'imdb_cache_dir_is_not_writeable' => 'imdb 缓存目录不可写',
|
||||
'imdb_photo_dir_can_not_create' => 'imdb 图片目录无法创建',
|
||||
'imdb_photo_dir_is_not_writeable' => 'imdb 图片目录不可写',
|
||||
];
|
||||
|
||||
@@ -27,6 +27,11 @@ return [
|
||||
\App\Models\BonusLogs::BUSINESS_TYPE_RECEIVE_REWARD => '收到獎勵',
|
||||
\App\Models\BonusLogs::BUSINESS_TYPE_RECEIVE_GIFT => '收到禮物',
|
||||
\App\Models\BonusLogs::BUSINESS_TYPE_UPLOAD_TORRENT => '發布種子',
|
||||
\App\Models\BonusLogs::BUSINESS_TYPE_SEEDING_BASIC => '做種基礎魔力',
|
||||
\App\Models\BonusLogs::BUSINESS_TYPE_SEEDING_DONOR_ADDITION => '做種捐贈加成',
|
||||
\App\Models\BonusLogs::BUSINESS_TYPE_SEEDING_OFFICIAL_ADDITION => '做種官種加成',
|
||||
\App\Models\BonusLogs::BUSINESS_TYPE_SEEDING_HAREM_ADDITION => '做種後宮加成',
|
||||
\App\Models\BonusLogs::BUSINESS_TYPE_SEEDING_MEDAL_ADDITION => '做種勳章加成',
|
||||
],
|
||||
'fields' => [
|
||||
'business_type' => '業務類型',
|
||||
|
||||
@@ -105,4 +105,9 @@ return [
|
||||
'msg_you_can_download' => "上傳。\n下載請到",
|
||||
'msg_here' => "[b]這裏[/b]",
|
||||
'msg_offer' => "候選 ",
|
||||
'require_seed_section_menu_title' => '急需保種',
|
||||
'imdb_cache_dir_can_not_create' => 'imdb 緩存目錄無法創建',
|
||||
'imdb_cache_dir_is_not_writeable' => 'imdb 緩存目錄不可寫',
|
||||
'imdb_photo_dir_can_not_create' => 'imdb 圖片目錄無法創建',
|
||||
'imdb_photo_dir_is_not_writeable' => 'imdb 圖片目錄不可寫',
|
||||
];
|
||||
|
||||
Reference in New Issue
Block a user