Merge branch '1.7' into php8

This commit is contained in:
xiaomlove
2022-03-31 16:43:51 +08:00
140 changed files with 21731 additions and 273 deletions

View File

@@ -8,4 +8,14 @@ class Bookmark extends NexusModel
protected $table = 'bookmarks';
protected $fillable = ['userid', 'torrentid'];
public function torrent()
{
return $this->belongsTo(Torrent::class, 'torrentid');
}
public function user()
{
return $this->belongsTo(Torrent::class, 'userid');
}
}

12
app/Models/Cheater.php Normal file
View File

@@ -0,0 +1,12 @@
<?php
namespace App\Models;
class Cheater extends NexusModel
{
protected $fillable = [
'added', 'userid', 'torrentid', 'uploaded', 'downloaded', 'anctime', 'seeders', 'leechers', 'hit',
'dealtby', 'dealtwith', 'comment',
];
}

View File

@@ -3,6 +3,8 @@
namespace App\Models;
use Illuminate\Database\Eloquent\Builder;
class Comment extends NexusModel
{
protected $casts = [
@@ -10,6 +12,45 @@ class Comment extends NexusModel
'editdate' => 'datetime',
];
protected $fillable = ['user', 'torrent', 'added', 'text', 'ori_text', 'editedby', 'editdate', 'offer', 'request', 'anonymous'];
const TYPE_TORRENT = 'torrent';
const TYPE_REQUEST = 'request';
const TYPE_OFFER = 'offer';
const TYPE_MAPS = [
self::TYPE_TORRENT => [
'model' => Torrent::class,
'foreign_key' => 'torrent',
'target_name_field' => 'name',
'target_script' => 'details.php?id=%s'
],
self::TYPE_REQUEST => [
'model' => Request::class,
'foreign_key' => 'request',
'target_name_field' => 'request',
'target_script' => 'viewrequests.php?id=%s&req_details=1'
],
self::TYPE_OFFER => [
'model' => Offer::class,
'foreign_key' => 'offer',
'target_name_field' => 'name',
'target_script' => 'offers.php?id=%s&off_details=1'
],
];
public function scopeType(Builder $query, string $type, int $typeValue)
{
foreach (self::TYPE_MAPS as $key => $value) {
if ($type != $key) {
$query->where($value['foreign_key'], 0);
} else {
$query->where($value['foreign_key'], $typeValue);
}
}
return $query;
}
public function related_torrent()
{
return $this->belongsTo(Torrent::class, 'torrent');

View File

@@ -21,4 +21,20 @@ class NexusModel extends Model
return $date->format($this->dateFormat ?: 'Y-m-d H:i:s');
}
/**
* Check is valid date string
*
* @see https://stackoverflow.com/questions/19271381/correctly-determine-if-date-string-is-a-valid-date-in-that-format
* @param $name
* @param string $format
* @return bool
*/
public function isValidDate($name, $format = 'Y-m-d H:i:s'): bool
{
$date = $this->getRawOriginal($name);
$d = \DateTime::createFromFormat($format, $date);
// The Y ( 4 digits year ) returns TRUE for any integer with any number of digits so changing the comparison from == to === fixes the issue.
return $d && $d->format($format) === $date;
}
}

19
app/Models/Offer.php Normal file
View File

@@ -0,0 +1,19 @@
<?php
namespace App\Models;
class Offer extends NexusModel
{
protected $fillable = ['userid', 'name', 'descr', 'comments', 'added'];
protected $casts = [
'added' => 'datetime'
];
public function user()
{
return $this->belongsTo(User::class, 'userid');
}
}

View File

@@ -4,9 +4,15 @@ namespace App\Models;
use Illuminate\Database\Eloquent\Builder;
use Illuminate\Support\Facades\Cache;
class Peer extends NexusModel
{
protected $fillable = [
'torrent', 'peer_id', 'ip', 'port', 'uploaded', 'downloaded', 'to_go', 'seeder', 'started', 'last_action',
'prev_action', 'connectable', 'userid', 'agent', 'finishedat', 'downloadoffset', 'uploadedoffset', 'passkey',
];
const CONNECTABLE_YES = 'yes';
const CONNECTABLE_NO = 'no';
@@ -71,4 +77,32 @@ class Peer extends NexusModel
{
return $this->belongsTo(Torrent::class, 'torrent');
}
/**
*
*/
public function updateConnectableStateIfNeeded()
{
$tmp_ip = $this->ip;
// IPv6 Check
if (filter_var($tmp_ip, FILTER_VALIDATE_IP, FILTER_FLAG_IPV6)) {
$tmp_ip = '['.$tmp_ip.']';
}
$cacheKey = 'peers:connectable:'.$tmp_ip.'-'.$this->port.'-'.$this->agent;
$log = "cacheKey: $cacheKey";
if (!Cache::has($cacheKey)) {
$con = @fsockopen($tmp_ip, $this->port, $error_code, $error_message, 1);
if (is_resource($con)) {
$this->connectable = self::CONNECTABLE_YES;
fclose($con);
} else {
$this->connectable = self::CONNECTABLE_NO;
}
Cache::put($cacheKey, $this->connectable, 600);
$log .= ", do check, connectable: " . $this->connectable;
} else {
$log .= ", don't do check";
}
do_log($log);
}
}

21
app/Models/Poll.php Normal file
View File

@@ -0,0 +1,21 @@
<?php
namespace App\Models;
class Poll extends NexusModel
{
protected $fillable = ['added', 'question', 'option0', 'option1', 'option2', 'option3', 'option4', 'option5'];
protected $casts = [
'added' => 'datetime'
];
const MAX_OPTION_INDEX = 19;
public function answers()
{
return $this->hasMany(PollAnswer::class, 'pollid');
}
}

22
app/Models/PollAnswer.php Normal file
View File

@@ -0,0 +1,22 @@
<?php
namespace App\Models;
class PollAnswer extends NexusModel
{
protected $table = 'pollanswers';
protected $fillable = ['pollid', 'userid', 'selection',];
public function poll()
{
return $this->belongsTo(Poll::class, 'pollid');
}
public function user()
{
return $this->belongsTo(User::class, 'userid');
}
}

19
app/Models/Request.php Normal file
View File

@@ -0,0 +1,19 @@
<?php
namespace App\Models;
class Request extends NexusModel
{
protected $fillable = ['userid', 'request', 'descr', 'comments', 'hits', 'added'];
protected $casts = [
'added' => 'datetime'
];
public function user()
{
return $this->belongsTo(User::class, 'userid');
}
}

18
app/Models/Reward.php Normal file
View File

@@ -0,0 +1,18 @@
<?php
namespace App\Models;
class Reward extends NexusModel
{
protected $table = 'magic';
protected $fillable = ['torrentid', 'userid', 'value', ];
public $timestamps = true;
public function user()
{
return $this->belongsTo(User::class, 'userid');
}
}

View File

@@ -3,6 +3,8 @@
namespace App\Models;
use Illuminate\Support\Arr;
use Illuminate\Support\Facades\Cache;
use Nexus\Database\NexusDB;
class Setting extends NexusModel
{
@@ -10,9 +12,9 @@ class Setting extends NexusModel
public static function get($name = null)
{
static $settings;
if (is_null($settings)) {
$settings = NexusDB::remember("nexus_settings_in_laravel", 10, function () {
$rows = self::query()->get(['name', 'value']);
$result = [];
foreach ($rows as $row) {
$value = $row->value;
if (!is_null($value)) {
@@ -21,9 +23,10 @@ class Setting extends NexusModel
$value = $arr;
}
}
Arr::set($settings, $row->name, $value);
Arr::set($result, $row->name, $value);
}
}
return $result;
});
if (is_null($name)) {
return $settings;
}

View File

@@ -9,6 +9,11 @@ class Snatch extends NexusModel
{
protected $table = 'snatched';
protected $fillable = [
'torrentid', 'userid', 'ip', 'port', 'uploaded', 'downloaded', 'to_go', 'seedtime', 'leechtime',
'last_action', 'startdat', 'completedat', 'finished'
];
protected $casts = [
'last_action' => 'datetime',
'startdat' => 'datetime',

View File

@@ -7,7 +7,7 @@ class Tag extends NexusModel
public $timestamps = true;
protected $fillable = [
'id', 'name', 'color', 'priority', 'created_at', 'updated_at'
'id', 'name', 'color', 'priority', 'created_at', 'updated_at', 'font_size', 'font_color', 'padding', 'margin', 'border_radius'
];
const DEFAULTS = [

View File

@@ -5,6 +5,8 @@ namespace App\Models;
class Thank extends NexusModel
{
protected $fillable = ['torrentid', 'userid'];
public function user()
{
return $this->belongsTo(User::class, 'userid');

View File

@@ -3,6 +3,8 @@
namespace App\Models;
use App\Repositories\TagRepository;
use JeroenG\Explorer\Application\Explored;
use Laravel\Scout\Searchable;
class Torrent extends NexusModel
{
@@ -26,6 +28,11 @@ class Torrent extends NexusModel
'added' => 'datetime'
];
public static $commentFields = [
'id', 'name', 'added', 'visible', 'banned', 'owner', 'sp_state', 'pos_state', 'hr', 'picktype', 'picktime',
'last_action', 'leechers', 'seeders', 'times_completed', 'views', 'size'
];
public static $basicRelations = [
'basic_category', 'basic_audio_codec', 'basic_codec', 'basic_media',
'basic_source', 'basic_standard', 'basic_team',
@@ -61,16 +68,79 @@ class Torrent extends NexusModel
const PROMOTION_HALF_DOWN_TWO_TIMES_UP = 6;
const PROMOTION_ONE_THIRD_DOWN = 7;
public static $promotionTypes = [
self::PROMOTION_NORMAL => ['text' => 'Normal', 'up_multiplier' => 1, 'down_multiplier' => 1],
self::PROMOTION_FREE => ['text' => 'Free', 'up_multiplier' => 1, 'down_multiplier' => 0],
self::PROMOTION_TWO_TIMES_UP => ['text' => '2X', 'up_multiplier' => 2, 'down_multiplier' => 1],
self::PROMOTION_FREE_TWO_TIMES_UP => ['text' => '2X Free', 'up_multiplier' => 2, 'down_multiplier' => 0],
self::PROMOTION_HALF_DOWN => ['text' => '50%', 'up_multiplier' => 1, 'down_multiplier' => 0.5],
self::PROMOTION_HALF_DOWN_TWO_TIMES_UP => ['text' => '2X 50%', 'up_multiplier' => 2, 'down_multiplier' => 0.5],
self::PROMOTION_ONE_THIRD_DOWN => ['text' => '30%', 'up_multiplier' => 1, 'down_multiplier' => 0.3],
public static array $promotionTypes = [
self::PROMOTION_NORMAL => [
'text' => 'Normal',
'up_multiplier' => 1,
'down_multiplier' => 1,
'color' => ''
],
self::PROMOTION_FREE => [
'text' => 'Free',
'up_multiplier' => 1,
'down_multiplier' => 0,
'color' => 'linear-gradient(to right, rgba(0,52,206,0.5), rgba(0,52,206,1), rgba(0,52,206,0.5))'
],
self::PROMOTION_TWO_TIMES_UP => [
'text' => '2X',
'up_multiplier' => 2,
'down_multiplier' => 1,
'color' => 'linear-gradient(to right, rgba(0,153,0,0.5), rgba(0,153,0,1), rgba(0,153,0,0.5))'
],
self::PROMOTION_FREE_TWO_TIMES_UP => [
'text' => '2X Free',
'up_multiplier' => 2,
'down_multiplier' => 0,
'color' => 'linear-gradient(to right, rgba(0,153,0,1), rgba(0,52,206,1)'
],
self::PROMOTION_HALF_DOWN => [
'text' => '50%',
'up_multiplier' => 1,
'down_multiplier' => 0.5,
'color' => 'linear-gradient(to right, rgba(220,0,3,0.5), rgba(220,0,3,1), rgba(220,0,3,0.5))'
],
self::PROMOTION_HALF_DOWN_TWO_TIMES_UP => [
'text' => '2X 50%',
'up_multiplier' => 2,
'down_multiplier' => 0.5,
'color' => 'linear-gradient(to right, rgba(0,153,0,1), rgba(220,0,3,1)'
],
self::PROMOTION_ONE_THIRD_DOWN => [
'text' => '30%',
'up_multiplier' => 1,
'down_multiplier' => 0.3,
'color' => 'linear-gradient(to right, rgba(65,23,73,0.5), rgba(65,23,73,1), rgba(65,23,73,0.5))'
],
];
const PICK_NORMAL = 'normal';
const PICK_HOT = 'hot';
const PICK_CLASSIC = 'classic';
const PICK_RECOMMENDED = 'recommended';
public static array $pickTypes = [
self::PICK_NORMAL => ['text' => self::PICK_NORMAL, 'color' => ''],
self::PICK_HOT => ['text' => self::PICK_HOT, 'color' => '#e78d0f'],
self::PICK_CLASSIC => ['text' => self::PICK_CLASSIC, 'color' => '#77b300'],
self::PICK_RECOMMENDED => ['text' => self::PICK_RECOMMENDED, 'color' => '#820084'],
];
const BONUS_REWARD_VALUES = [50, 100, 200, 500, 1000];
public function getPickInfoAttribute()
{
$info = self::$pickTypes[$this->picktype] ?? null;
if ($info) {
$info['text'] = nexus_trans('torrent.pick_info.' . $this->picktype);
return $info;
}
}
public function getPromotionInfoAttribute()
{
return self::$promotionTypes[$this->sp_state_real] ?? null;
}
public function getSpStateRealTextAttribute()
{
$spStateReal = $this->sp_state_real;
@@ -79,6 +149,9 @@ class Torrent extends NexusModel
public function getSpStateRealAttribute()
{
if ($this->getRawOriginal('sp_state') === null) {
throw new \RuntimeException('no select sp_state field');
}
$spState = $this->sp_state;
$global = self::getGlobalPromotionState();
$log = sprintf('torrent: %s sp_state: %s, global sp state: %s', $this->id, $spState, $global);
@@ -143,7 +216,10 @@ class Torrent extends NexusModel
public static function getFieldLabels(): array
{
$fields = ['comments', 'times_completed', 'peers_count', 'thank_users_count', 'numfiles', 'bookmark_yes', 'bookmark_no'];
$fields = [
'comments', 'times_completed', 'peers_count', 'thank_users_count', 'numfiles', 'bookmark_yes', 'bookmark_no',
'reward_yes', 'reward_no', 'reward_logs', 'download', 'thanks_yes', 'thanks_no'
];
$result = [];
foreach($fields as $field) {
$result[$field] = nexus_trans("torrent.show.{$field}_label");
@@ -163,6 +239,11 @@ class Torrent extends NexusModel
return true;
}
public function bookmarks(): \Illuminate\Database\Eloquent\Relations\HasMany
{
return $this->hasMany(Bookmark::class, 'torrentid');
}
public function user()
{
return $this->belongsTo(User::class, 'owner')->withDefault(User::getDefaultUserAttributes());
@@ -263,9 +344,19 @@ class Torrent extends NexusModel
$query->where('visible', $visible);
}
public function torrent_tags(): \Illuminate\Database\Eloquent\Relations\HasMany
{
return $this->hasMany(TorrentTag::class, 'torrent_id');
}
public function tags(): \Illuminate\Database\Eloquent\Relations\BelongsToMany
{
return $this->belongsToMany(Tag::class, 'torrent_tags', 'torrent_id', 'tag_id')
->orderByRaw(sprintf("field(`tags`.`id`,%s)", TagRepository::getOrderByFieldIdString()));
}
public function reward_logs(): \Illuminate\Database\Eloquent\Relations\HasMany
{
return $this->hasMany(Reward::class, 'torrentid');
}
}

View File

@@ -79,6 +79,32 @@ class User extends Authenticatable
'invites' => '邀请',
];
public function mappableAs(): array
{
return [
'id' => 'long',
'username' => [
'type' => 'text',
'analyzer' => 'ik_max_word',
],
'email' => [
'type' => 'text',
'analyzer' => 'ik_max_word',
],
'added' => 'date',
];
}
public function toSearchableArray()
{
return [
'id' => $this->id,
'username' => $this->username,
'email' => $this->email,
'added' => $this->added,
];
}
public function getClassTextAttribute(): string
{
return self::$classes[$this->class]['text'] ?? '';
@@ -116,7 +142,7 @@ class User extends Authenticatable
*/
protected $fillable = [
'username', 'email', 'passhash', 'secret', 'stylesheet', 'editsecret', 'added', 'modcomment', 'enabled', 'status',
'leechwarn', 'leechwarnuntil', 'page', 'class'
'leechwarn', 'leechwarnuntil', 'page', 'class', 'uploaded', 'downloaded', 'clientselect', 'showclienterror',
];
/**
@@ -320,8 +346,10 @@ class User extends Authenticatable
public function medals(): \Illuminate\Database\Eloquent\Relations\BelongsToMany
{
return $this->belongsToMany(Medal::class, 'user_medals', 'uid', 'medal_id')
->withPivot(['id', 'expire_at'])
->withTimestamps();
->withPivot(['id', 'expire_at', 'status'])
->withTimestamps()
->orderByPivot('id', 'desc')
;
}
public function valid_medals(): \Illuminate\Database\Eloquent\Relations\BelongsToMany
@@ -331,6 +359,26 @@ class User extends Authenticatable
});
}
public function wearing_medals(): \Illuminate\Database\Eloquent\Relations\BelongsToMany
{
return $this->valid_medals()->where('user_medals.status', UserMedal::STATUS_WEARING);
}
public function reward_torrent_logs(): \Illuminate\Database\Eloquent\Relations\HasMany
{
return $this->hasMany(Reward::class, 'userid');
}
public function thank_torrent_logs(): \Illuminate\Database\Eloquent\Relations\HasMany
{
return $this->hasMany(Thank::class, 'userid');
}
public function poll_answers(): \Illuminate\Database\Eloquent\Relations\HasMany
{
return $this->hasMany(PollAnswer::class, 'userid');
}
public function getAvatarAttribute($value)
{
if ($value) {
@@ -348,7 +396,7 @@ class User extends Authenticatable
public function updateWithModComment(array $update, $modComment)
{
if (!$this->exists) {
throw new \RuntimeException('This mehtod only works when user exists!');
throw new \RuntimeException('This method only works when user exists!');
}
//@todo how to do prepare bindings here ?
$modComment = addslashes($modComment);

View File

@@ -4,5 +4,10 @@ namespace App\Models;
class UserMedal extends NexusModel
{
protected $fillable = ['uid', 'medal_id', 'expire_at'];
protected $fillable = ['uid', 'medal_id', 'expire_at', 'status'];
const STATUS_NOT_WEARING = 0;
const STATUS_WEARING = 1;
}