diff --git a/admin/src/views/user/form.vue b/admin/src/views/user/form.vue index 962c72c6..a6d84616 100644 --- a/admin/src/views/user/form.vue +++ b/admin/src/views/user/form.vue @@ -20,6 +20,22 @@ + + + + + + + + + + + Submit @@ -47,11 +63,14 @@ export default { const { id } = route.query const state = reactive({ id: id, + userClasses: [], formData: { username: '', email: '', password: '', password_confirmation: '', + id: '', + class: '' }, rules: { username: [ @@ -68,8 +87,8 @@ export default { ], }, }) - onMounted( () => { - + onMounted( async () => { + await listAllClass() }) onBeforeUnmount(() => { @@ -83,6 +102,12 @@ export default { } }) } + + const listAllClass = async () => { + let res = await api.listClass() + state.userClasses = res.data + } + return { ...toRefs(state), formRef, diff --git a/app/Http/Resources/TorrentResource.php b/app/Http/Resources/TorrentResource.php index e787bc64..d2baf86d 100644 --- a/app/Http/Resources/TorrentResource.php +++ b/app/Http/Resources/TorrentResource.php @@ -33,9 +33,16 @@ class TorrentResource extends JsonResource 'seeders' => $this->seeders, 'times_completed' => $this->times_completed, 'numfiles' => $this->numfiles, + 'sp_state' => $this->sp_state, + 'sp_state_real' => $this->sp_state_real, + 'sp_state_real_text' => $this->spStateRealText, + 'hr' => $this->hr, + 'pick_type' => $this->picktype, + 'pick_time' => $this->picktime, 'download_url' => $this->download_url, 'user' => new UserResource($this->whenLoaded('user')), 'basic_category' => new CategoryResource($this->whenLoaded('basic_category')), + 'tags' => TagResource::collection($this->whenLoaded('tags')), ]; $descriptionArr = format_description($this->descr); $out['cover'] = get_image_from_description($descriptionArr, true); diff --git a/app/Models/Torrent.php b/app/Models/Torrent.php index 85d9ded0..e29e0353 100644 --- a/app/Models/Torrent.php +++ b/app/Models/Torrent.php @@ -2,6 +2,8 @@ namespace App\Models; +use App\Repositories\TagRepository; + class Torrent extends NexusModel { protected $fillable = [ @@ -9,9 +11,11 @@ class Torrent extends NexusModel 'category', 'source', 'medium', 'codec', 'standard', 'processing', 'team', 'audiocodec', 'size', 'added', 'type', 'numfiles', 'owner', 'nfo', 'sp_state', 'promotion_time_type', 'promotion_until', 'anonymous', 'url', 'pos_state', 'cache_stamp', 'picktype', 'picktime', - 'last_reseed', 'pt_gen', 'tags', 'technical_info' + 'last_reseed', 'pt_gen', 'technical_info' ]; + private static $globalPromotionState; + const VISIBLE_YES = 'yes'; const VISIBLE_NO = 'no'; @@ -49,6 +53,59 @@ class Torrent extends NexusModel self::HR_YES => ['text' => 'YES'], ]; + const PROMOTION_NORMAL = 1; + const PROMOTION_FREE = 2; + const PROMOTION_TWO_TIMES_UP = 3; + const PROMOTION_FREE_TWO_TIMES_UP = 4; + const PROMOTION_HALF_DOWN = 5; + 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 function getSpStateRealTextAttribute() + { + $spStateReal = $this->sp_state_real; + return self::$promotionTypes[$spStateReal]['text'] ?? ''; + } + + public function getSpStateRealAttribute() + { + $spState = $this->sp_state; + $global = self::getGlobalPromotionState(); + $log = sprintf('torrent: %s sp_state: %s, global sp state: %s', $this->id, $spState, $global); + if ($global != self::PROMOTION_NORMAL) { + $spState = $global; + $log .= sprintf(", global != %s, set sp_state to global: %s", self::PROMOTION_NORMAL, $global); + } + if (!isset(self::$promotionTypes[$spState])) { + $log .= ", but now sp_state: $spState, is invalid, reset to: " . self::PROMOTION_NORMAL; + $spState = self::PROMOTION_NORMAL; + } + do_log($log, 'debug'); + return $spState; + } + + public static function getGlobalPromotionState() + { + if (is_null(self::$globalPromotionState)) { + $result = TorrentState::query()->first(['global_sp_state']); + if (!$result) { + do_log("global sp state no value", 'error'); + } + self::$globalPromotionState = $result->global_sp_state ?? false; + } + return self::$globalPromotionState; + } + public function getHrAttribute(): string { $hrMode = Setting::get('hr.mode'); @@ -208,6 +265,7 @@ class Torrent extends NexusModel public function tags(): \Illuminate\Database\Eloquent\Relations\BelongsToMany { - return $this->belongsToMany(Tag::class, 'torrent_tags', 'torrent_id', 'tag_id'); + return $this->belongsToMany(Tag::class, 'torrent_tags', 'torrent_id', 'tag_id') + ->orderByRaw(sprintf("field(`tags`.`id`,%s)", TagRepository::getOrderByFieldIdString())); } } diff --git a/app/Models/TorrentState.php b/app/Models/TorrentState.php new file mode 100644 index 00000000..fbe8f7b7 --- /dev/null +++ b/app/Models/TorrentState.php @@ -0,0 +1,11 @@ +createBasicQuery(); @@ -41,7 +43,7 @@ class TagRepository extends BaseRepository return $result; } - public function createBasicQuery() + public static function createBasicQuery() { return Tag::query()->orderBy('priority', 'desc')->orderBy('id', 'desc'); } @@ -127,5 +129,14 @@ class TagRepository extends BaseRepository return count($values); } + public static function getOrderByFieldIdString() + { + if (is_null(self::$orderByFieldIdString)) { + $results = self::createBasicQuery()->get(['id']); + self::$orderByFieldIdString = $results->isEmpty() ? '0' : $results->implode('id', ','); + } + return self::$orderByFieldIdString; + } + } diff --git a/app/Repositories/TorrentRepository.php b/app/Repositories/TorrentRepository.php index 6a98285a..caf75f9c 100644 --- a/app/Repositories/TorrentRepository.php +++ b/app/Repositories/TorrentRepository.php @@ -82,7 +82,7 @@ class TorrentRepository extends BaseRepository $query = $this->handleGetListSort($query, $params); - $with = ['user']; + $with = ['user', 'tags']; $torrents = $query->with($with)->paginate(); $userArr = $user->toArray(); foreach($torrents as &$item) { diff --git a/app/Repositories/UserRepository.php b/app/Repositories/UserRepository.php index a70a4250..a9b990f3 100644 --- a/app/Repositories/UserRepository.php +++ b/app/Repositories/UserRepository.php @@ -66,27 +66,61 @@ class UserRepository extends BaseRepository ]; } + /** + * create user + * + * @param array $params must: username, email, password, password_confirmation. optional: id, class + * @return User + */ public function store(array $params) { $password = $params['password']; if ($password != $params['password_confirmation']) { throw new \InvalidArgumentException("password confirmation != password"); } - + $username = $params['username']; + if (!validusername($username)) { + throw new \InvalidArgumentException("Innvalid username: $username"); + } + $email = htmlspecialchars(trim($params['email'])); + $email = safe_email($email); + if (!check_email($email)) { + throw new \InvalidArgumentException("Innvalid email: $email"); + } + if (User::query()->where('email', $email)->exists()) { + throw new \InvalidArgumentException("The email address: $email is already in use"); + } + if (mb_strlen($password) < 6 || mb_strlen($password) > 40) { + throw new \InvalidArgumentException("Innvalid password: $password, it should be more than 6 character and less than 40 character"); + } + $class = !empty($params['class']) ? intval($params['class']) : User::CLASS_USER; + if (!isset(User::$classes[$class])) { + throw new \InvalidArgumentException("Invalid user class: $class"); + } $setting = Setting::get('main'); $secret = mksecret(); $passhash = md5($secret . $password . $secret); $data = [ - 'username' => $params['username'], - 'email' => $params['email'], + 'username' => $username, + 'email' => $email, 'secret' => $secret, 'editsecret' => '', 'passhash' => $passhash, 'stylesheet' => $setting['defstylesheet'], 'added' => now()->toDateTimeString(), 'status' => User::STATUS_CONFIRMED, + 'class' => $class ]; - $user = User::query()->create($data); + $user = new User($data); + if ($params['id']) { + if (User::query()->where('id', $params['id'])->exists()) { + throw new \InvalidArgumentException("uid: {$params['id']} already exists."); + } + do_log("[CREATE_USER], specific id: " . $params['id']); + $user->id = $params['id']; + } + $user->save(); + return $user; } diff --git a/database/migrations/2021_06_08_113437_create_users_table.php b/database/migrations/2021_06_08_113437_create_users_table.php index 444fc4d7..dc9d78f2 100644 --- a/database/migrations/2021_06_08_113437_create_users_table.php +++ b/database/migrations/2021_06_08_113437_create_users_table.php @@ -17,7 +17,7 @@ class CreateUsersTable extends Migration return; } Schema::create('users', function (Blueprint $table) { - $table->increments('id'); + $table->id('id')->startingValue(10001); $table->string('username', 40)->default('')->unique('username'); $table->string('passhash', 32)->default(''); $table->binary('secret'); @@ -133,6 +133,7 @@ class CreateUsersTable extends Migration $table->enum('showfb', ['yes', 'no'])->default('yes'); $table->string('page')->nullable()->default(''); $table->index(['status', 'added'], 'status_added'); + }); } diff --git a/include/cleanup.php b/include/cleanup.php index f472a5cb..6b8d3749 100644 --- a/include/cleanup.php +++ b/include/cleanup.php @@ -168,7 +168,9 @@ function user_to_peasant($down_floor_gb, $minratio){ function ban_user_with_leech_warning_expired() { $dt = date("Y-m-d H:i:s"); // take date time + // VIP or above won't effect $results = \App\Models\User::query() + ->where('class', '<', \App\Models\User::CLASS_VIP) ->where('enabled', \App\Models\User::ENABLED_YES) ->where('leechwarn', 'yes') ->where('leechwarnuntil', '<', $dt) diff --git a/nexus/Install/Install.php b/nexus/Install/Install.php index cabfedc4..72457251 100644 --- a/nexus/Install/Install.php +++ b/nexus/Install/Install.php @@ -2,6 +2,8 @@ namespace Nexus\Install; +use App\Models\User; +use App\Repositories\UserRepository; use Illuminate\Support\Str; use Nexus\Database\NexusDB; @@ -384,44 +386,22 @@ class Install public function createAdministrator($username, $email, $password, $confirmPassword) { - $count = get_row_count('users', 'where class = 16'); + $class = User::CLASS_STAFF_LEADER; + $count = get_row_count('users', 'where class = ' . $class); if ($count > 0) { throw new \InvalidArgumentException("Administrator already exists"); } - if (!validusername($username)) { - throw new \InvalidArgumentException("Innvalid username: $username"); - } - $email = htmlspecialchars(trim($email)); - $email = safe_email($email); - if (!check_email($email)) { - throw new \InvalidArgumentException("Innvalid email: $email"); - } - $res = sql_query("SELECT id FROM users WHERE email=" . sqlesc($email)); - $arr = mysql_fetch_row($res); - if ($arr) { - throw new \InvalidArgumentException("The email address: $email is already in use"); - } - if (mb_strlen($password) < 6 || mb_strlen($password) > 40) { - throw new \InvalidArgumentException("Innvalid password: $password, it should be more than 6 character and less than 40 character"); - } - if ($password != $confirmPassword) { - throw new \InvalidArgumentException("confirmPassword: $confirmPassword != password"); - } - $setting = get_setting('main'); - $secret = mksecret(); - $passhash = md5($secret . $password . $secret); - $insert = [ + $data = [ 'username' => $username, - 'passhash' => $passhash, - 'secret' => $secret, 'email' => $email, - 'stylesheet' => $setting['defstylesheet'], - 'class' => 16, - 'status' => 'confirmed', - 'added' => date('Y-m-d H:i:s'), + 'password' => $password, + 'password_confirmation' => $confirmPassword, + 'class' => $class, + 'id' => 1, ]; - $this->doLog("[CREATE ADMINISTRATOR] " . json_encode($insert)); - return NexusDB::insert('users', $insert); + $user = (new UserRepository())->store($data); + $this->doLog("[CREATE ADMINISTRATOR] " . $user->toJson()); + return $user; } public function createEnvFile($data, $scene = 'install')