From 47f64f2c5c07cd330a7683132afe54eab014d4ee Mon Sep 17 00:00:00 2001 From: xiaomlove <353856593@qq.com> Date: Fri, 14 May 2021 20:41:43 +0800 Subject: [PATCH] [admin] add setting backup --- admin/src/App.vue | 6 +- admin/src/styles/common.scss | 3 + admin/src/utils/api.js | 21 +++ admin/src/views/setting/form-backup.vue | 157 ++++++++++++++++++ admin/src/views/setting/form-basic.vue | 3 - admin/src/views/setting/index.vue | 19 ++- admin/src/views/user/detail.vue | 73 +++++++- admin/src/views/user/dialog-disable-user.vue | 77 +++++++++ admin/src/views/user/dialog-invite-info.vue | 63 +++++++ admin/src/views/user/dialog-mod-comment.vue | 56 +++++++ .../src/views/user/dialog-reset-password.vue | 83 +++++++++ app/Console/Commands/Test.php | 6 +- app/Console/Commands/UserResetPassword.php | 10 +- app/Http/Controllers/SettingController.php | 77 +++++++++ app/Http/Controllers/UserController.php | 37 ++++- app/Http/Resources/InviteResource.php | 2 + app/Http/Resources/UserResource.php | 1 + app/Models/Invite.php | 10 ++ app/Models/User.php | 11 +- app/Repositories/SettingRepository.php | 64 +++++++ app/Repositories/UserRepository.php | 39 ++++- include/globalfunctions.php | 2 +- routes/api.php | 6 + 23 files changed, 787 insertions(+), 39 deletions(-) create mode 100644 admin/src/views/setting/form-backup.vue create mode 100644 admin/src/views/user/dialog-disable-user.vue create mode 100644 admin/src/views/user/dialog-invite-info.vue create mode 100644 admin/src/views/user/dialog-mod-comment.vue create mode 100644 admin/src/views/user/dialog-reset-password.vue create mode 100644 app/Http/Controllers/SettingController.php create mode 100644 app/Repositories/SettingRepository.php diff --git a/admin/src/App.vue b/admin/src/App.vue index 3238e24b..90eb1ae7 100644 --- a/admin/src/App.vue +++ b/admin/src/App.vue @@ -38,9 +38,9 @@ Exam user - - - + + Setting + diff --git a/admin/src/styles/common.scss b/admin/src/styles/common.scss index d2fdccc0..68a1a61c 100644 --- a/admin/src/styles/common.scss +++ b/admin/src/styles/common.scss @@ -5,3 +5,6 @@ .nexus-help-text { color: #aaa; } +.pre-line { + white-space: pre-line; +} diff --git a/admin/src/utils/api.js b/admin/src/utils/api.js index 0f21f7b5..0bb1ff49 100644 --- a/admin/src/utils/api.js +++ b/admin/src/utils/api.js @@ -32,9 +32,24 @@ const api = { getUserBase: (params = {}) => { return axios.get('user-base', {params: params}); }, + getInviteInfo: (params = {}) => { + return axios.get('user-invite-info', {params: params}); + }, + getUserModComment: (params = {}) => { + return axios.get('user-mod-comment', {params: params}); + }, storeUser: (params = {}) => { return axios.post('users', params); }, + disableUser: (params = {}) => { + return axios.post('user-disable', params); + }, + enableUser: (params = {}) => { + return axios.post('user-enable', params); + }, + resetPassword: (params = {}) => { + return axios.post('user-reset-password', params); + }, listUserMatchExams: (params = {}) => { return axios.get('user-match-exams', {params: params}); }, @@ -69,6 +84,12 @@ const api = { storeExamUser: (params) => { return axios.post('exam-users', params); }, + storeSetting: (params) => { + return axios.post('settings', params); + }, + listSetting: (params) => { + return axios.get('settings', {params}); + }, } export default api diff --git a/admin/src/views/setting/form-backup.vue b/admin/src/views/setting/form-backup.vue new file mode 100644 index 00000000..a45178f5 --- /dev/null +++ b/admin/src/views/setting/form-backup.vue @@ -0,0 +1,157 @@ + + + + + diff --git a/admin/src/views/setting/form-basic.vue b/admin/src/views/setting/form-basic.vue index 7f68be20..d39cf02e 100644 --- a/admin/src/views/setting/form-basic.vue +++ b/admin/src/views/setting/form-basic.vue @@ -24,7 +24,6 @@ export default { name: 'SettingFormBasic', setup() { const { proxy } = getCurrentInstance() - console.log('proxy', proxy) const formRef = ref(null) const route = useRoute() const router = useRouter() @@ -45,8 +44,6 @@ export default { }, }) onMounted( () => { - listAllClass() - listAllIndex() if (id) { api.getExam(id).then(res => { state.formData.name = res.data.name diff --git a/admin/src/views/setting/index.vue b/admin/src/views/setting/index.vue index a8b0d9c2..f234acfd 100644 --- a/admin/src/views/setting/index.vue +++ b/admin/src/views/setting/index.vue @@ -1,8 +1,7 @@ @@ -14,21 +13,22 @@ import api from '../../utils/api' import { useTable, renderTableData } from '../../utils/table' import FormBasic from './form-basic.vue' import FormMain from './form-main.vue' +import FormBackup from './form-backup.vue' export default { name: 'Setting', components: { - FormBasic, FormMain, + FormBasic, FormMain, FormBackup }, setup() { const multipleTable = ref(null) const router = useRouter() - + const backup = ref(null) const state = useTable() onMounted(() => { - console.log('ExamTable onMounted') - fetchTableData() + console.log('Setting onMounted') + backup.value.listSetting() }) const fetchTableData = async () => { state.loading = true @@ -55,6 +55,9 @@ export default { state.query.page = val fetchTableData() } + const handleTabClick = (val) => { + console.log('handleTabClick', val) + } return { ...toRefs(state), multipleTable, @@ -62,8 +65,10 @@ export default { handleAdd, handleEdit, handleDelete, + handleTabClick, fetchTableData, changePage, + backup, } } } diff --git a/admin/src/views/user/detail.vue b/admin/src/views/user/detail.vue index 70b716fc..78b38424 100644 --- a/admin/src/views/user/detail.vue +++ b/admin/src/views/user/detail.vue @@ -19,8 +19,8 @@
- Reset password - PM + Mod comment + Reset password Assign exam
@@ -30,6 +30,25 @@ {{baseInfo.email}} Change + + Enabled + {{baseInfo.enabled}} + + + + + Added {{baseInfo.added}} @@ -38,6 +57,11 @@ Class {{baseInfo.class_text}} + + Invite by + {{baseInfo.inviter && baseInfo.inviter.username}} + View + Uploaded {{baseInfo.uploaded_text}} @@ -113,6 +137,11 @@ + + + + + diff --git a/admin/src/views/user/dialog-invite-info.vue b/admin/src/views/user/dialog-invite-info.vue new file mode 100644 index 00000000..72b87856 --- /dev/null +++ b/admin/src/views/user/dialog-invite-info.vue @@ -0,0 +1,63 @@ + + + diff --git a/admin/src/views/user/dialog-mod-comment.vue b/admin/src/views/user/dialog-mod-comment.vue new file mode 100644 index 00000000..085f8171 --- /dev/null +++ b/admin/src/views/user/dialog-mod-comment.vue @@ -0,0 +1,56 @@ + + + diff --git a/admin/src/views/user/dialog-reset-password.vue b/admin/src/views/user/dialog-reset-password.vue new file mode 100644 index 00000000..9e512afd --- /dev/null +++ b/admin/src/views/user/dialog-reset-password.vue @@ -0,0 +1,83 @@ + + + diff --git a/app/Console/Commands/Test.php b/app/Console/Commands/Test.php index 229af31c..eea695a7 100644 --- a/app/Console/Commands/Test.php +++ b/app/Console/Commands/Test.php @@ -57,9 +57,9 @@ class Test extends Command // dd($r); // $rep->assignCronjob(); // $r = $rep->cronjobCheckout(); - $disk = Storage::disk('google_dirve'); - $r = $disk->put('/', base_path('composer.json')); - dd($r); +// $disk = Storage::disk('google_dirve'); +// $r = $disk->put('/', base_path('composer.json')); +// $r = DB::table('users')->where('id', 1)->update(['modcomment' => DB::raw("concat_ws(',', 'ddddd', modcomment)")]); } } diff --git a/app/Console/Commands/UserResetPassword.php b/app/Console/Commands/UserResetPassword.php index ee85ab8f..43b2fa01 100644 --- a/app/Console/Commands/UserResetPassword.php +++ b/app/Console/Commands/UserResetPassword.php @@ -12,14 +12,14 @@ class UserResetPassword extends Command * * @var string */ - protected $signature = 'user:reset_password {username} {password} {password_confirmation}'; + protected $signature = 'user:reset_password {uid} {password} {password_confirmation}'; /** * The console command description. * * @var string */ - protected $description = 'Reset user password, arguments: username password password_comfirmation'; + protected $description = 'Reset user password, arguments: uid password password_comfirmation'; /** * Create a new command instance. @@ -38,15 +38,15 @@ class UserResetPassword extends Command */ public function handle() { - $username = $this->argument('username'); + $uid = $this->argument('uid'); $password = $this->argument('password'); $passwordConfirmation = $this->argument('password_confirmation'); - $log = "username: $username, password: $password, passwordConfirmation: $passwordConfirmation"; + $log = "uid: $uid, password: $password, passwordConfirmation: $passwordConfirmation"; $this->info($log); do_log($log); $rep = new UserRepository(); - $result = $rep->resetPassword($username, $password, $passwordConfirmation); + $result = $rep->resetPassword($uid, $password, $passwordConfirmation); $log = sprintf('[%s], %s, result: %s', REQUEST_ID, __METHOD__, var_export($result, true)); $this->info($log); do_log($log); diff --git a/app/Http/Controllers/SettingController.php b/app/Http/Controllers/SettingController.php new file mode 100644 index 00000000..9fef65bf --- /dev/null +++ b/app/Http/Controllers/SettingController.php @@ -0,0 +1,77 @@ +repository = $repository; + } + + /** + * Display a listing of the resource. + * + * @param Request $request + * @return array + */ + public function index(Request $request) + { + $result = $this->repository->getList($request->all()); + return $this->success($result); + } + + /** + * Store a newly created resource in storage. + * + * @param \Illuminate\Http\Request $request + * @return array + */ + public function store(Request $request) + { + $result = $this->repository->store($request->all()); + return $this->success($result, 'Save setting success!'); + } + + /** + * Display the specified resource. + * + * @param int $id + * @return array + */ + public function show($id) + { + + } + + /** + * Update the specified resource in storage. + * + * @param \Illuminate\Http\Request $request + * @param int $id + * @return array + */ + public function update(Request $request, $id) + { + + } + + /** + * Remove the specified resource from storage. + * + * @param int $id + * @return array + */ + public function destroy($id) + { + + } + +} diff --git a/app/Http/Controllers/UserController.php b/app/Http/Controllers/UserController.php index 68511e1e..1a807ecc 100644 --- a/app/Http/Controllers/UserController.php +++ b/app/Http/Controllers/UserController.php @@ -3,6 +3,7 @@ namespace App\Http\Controllers; use App\Http\Resources\ExamResource; +use App\Http\Resources\InviteResource; use App\Http\Resources\UserResource; use App\Repositories\ExamRepository; use App\Repositories\UserRepository; @@ -90,14 +91,13 @@ class UserController extends Controller public function resetPassword(Request $request) { $rules = [ - 'username' => 'required|string|exists:users', + 'uid' => 'required', 'password' => 'required|string|min:6|max:40', 'password_confirmation' => 'required|same:password', ]; $request->validate($rules); - $result = $this->repository->resetPassword($request->repositoryname, $request->password, $request->password_confirmation); - $resource = new UserResource($result); - return $this->success($resource); + $result = $this->repository->resetPassword($request->uid, $request->password, $request->password_confirmation); + return $this->success($result, 'Reset password success!'); } public function classes() @@ -132,6 +132,35 @@ class UserController extends Controller 'reason' => 'required', ]); $result = $this->repository->disableUser(Auth::user(), $request->uid, $request->reason); + return $this->success($result, 'Disable user success!'); + } + + public function enable(Request $request) + { + $request->validate([ + 'uid' => 'required', + ]); + $result = $this->repository->enableUser(Auth::user(), $request->uid); + return $this->success($result, 'Enable user success!'); + } + + public function inviteInfo(Request $request) + { + $request->validate([ + 'uid' => 'required', + ]); + $result = $this->repository->getInviteInfo($request->uid); + $resource = $result ? (new InviteResource($result)) : null; + return $this->success($resource); + } + + public function modComment(Request $request) + { + $request->validate([ + 'uid' => 'required', + ]); + $result = $this->repository->getModComment($request->uid); return $this->success($result); } + } diff --git a/app/Http/Resources/InviteResource.php b/app/Http/Resources/InviteResource.php index 7fa4ebf6..45c0e514 100644 --- a/app/Http/Resources/InviteResource.php +++ b/app/Http/Resources/InviteResource.php @@ -25,6 +25,8 @@ class InviteResource extends JsonResource 'invitee_register_uid' => $this->invitee_register_uid, 'invitee_register_email' => $this->invitee_register_email, 'invitee_register_username' => $this->invitee_register_username, + 'inviter_user' => new UserResource($this->whenLoaded('inviter_user')), + 'invitee_user' => new UserResource($this->whenLoaded('invitee_user')), ]; } } diff --git a/app/Http/Resources/UserResource.php b/app/Http/Resources/UserResource.php index 54b13cac..4d5404a1 100644 --- a/app/Http/Resources/UserResource.php +++ b/app/Http/Resources/UserResource.php @@ -19,6 +19,7 @@ class UserResource extends JsonResource 'email' => $this->email, 'username' => $this->username, 'status' => $this->status, + 'enabled' => $this->enabled, 'added' => format_datetime($this->added), 'class' => $this->class, 'class_text' => $this->class_text, diff --git a/app/Models/Invite.php b/app/Models/Invite.php index 2bb96876..91fc38f4 100644 --- a/app/Models/Invite.php +++ b/app/Models/Invite.php @@ -24,4 +24,14 @@ class Invite extends NexusModel return self::$validInfo[$this->valid]['text'] ?? ''; } + public function inviter_user() + { + return $this->belongsTo(User::class, 'inviter'); + } + + public function invitee_user() + { + return $this->belongsTo(User::class, 'invitee_register_uid'); + } + } diff --git a/app/Models/User.php b/app/Models/User.php index 04776bb8..72848dbd 100644 --- a/app/Models/User.php +++ b/app/Models/User.php @@ -65,7 +65,6 @@ class User extends Authenticatable } - /** * 为数组 / JSON 序列化准备日期。 * @@ -82,7 +81,9 @@ class User extends Authenticatable * * @var array */ - protected $fillable = ['username', 'email', 'passhash', 'secret', 'editsecret', 'added']; + protected $fillable = [ + 'username', 'email', 'passhash', 'secret', 'stylesheet', 'editsecret', 'added', 'modcomment', 'enabled', 'status' + ]; /** * The attributes that should be hidden for arrays. @@ -105,7 +106,7 @@ class User extends Authenticatable public static $commonFields = [ 'id', 'username', 'email', 'class', 'status', 'added', 'avatar', 'uploaded', 'downloaded', 'seedbonus', 'seedtime', 'leechtime', - 'invited_by', + 'invited_by', 'enabled', ]; public function checkIsNormal(array $fields = ['status', 'enabled']) @@ -160,7 +161,9 @@ class User extends Authenticatable if (!$this->exists) { throw new \RuntimeException('This mehtod only works when user exists!'); } - $update['modcomment'] = DB::raw("concat_ws('\n', $modComment, modcomment)"); + //@todo how to do prepare bindings here ? + $modComment = addslashes($modComment); + $update['modcomment'] = DB::raw("concat_ws('\n', '$modComment', modcomment)"); return $this->update($update); } diff --git a/app/Repositories/SettingRepository.php b/app/Repositories/SettingRepository.php new file mode 100644 index 00000000..3b488c92 --- /dev/null +++ b/app/Repositories/SettingRepository.php @@ -0,0 +1,64 @@ +where('name', 'like', "{$params['prefix']}%"); + } + $settings = $query->get(); + $results = []; + foreach ($settings as $setting) { + $value = $setting->value; + $arr = json_decode($value, true); + if (is_array($arr)) { + $value = $arr; + } + Arr::set($results, $setting->name, $value); + } + return $results; + } + + public function store(array $params) + { + $settingModel = new Setting(); + $values = []; + foreach ($params as $prefix => $nameValues) { + if (!is_array($nameValues)) { + throw new \InvalidArgumentException("Unsupported parameter format."); + } + foreach ($nameValues as $name => $value) { + $valueArr = Arr::wrap($value); + array_walk_recursive($valueArr, function ($item) {return addslashes($item);}); + if (is_array($value)) { + $valueStr = json_encode($valueArr); + } else { + $valueStr = Arr::first($valueArr); + } + $values[] = sprintf("('%s', '%s')", addslashes("$prefix.$name"), addslashes($valueStr)); + } + } + if (empty($values)) { + do_log("no values"); + return true; + } + $sql = sprintf( + "insert into `%s` (`name`, `value`) values %s on duplicate key update `value` = values(`value`)", + $settingModel->getTable(), implode(', ', $values) + ); + $result = DB::insert($sql); + do_log("sql: $sql, result: $result"); + return $result; + } + +} diff --git a/app/Repositories/UserRepository.php b/app/Repositories/UserRepository.php index 0630250f..8105cd7d 100644 --- a/app/Repositories/UserRepository.php +++ b/app/Repositories/UserRepository.php @@ -1,6 +1,7 @@ where('username', $username)->firstOrFail(['id', 'username']); + $user = User::query()->findOrFail($id, ['id', 'username']); $secret = mksecret(); $passhash = md5($secret . $password . $secret); $update = [ @@ -87,7 +88,7 @@ class UserRepository extends BaseRepository 'passhash' => $passhash, ]; $user->update($update); - return $user; + return true; } public function listClass() @@ -101,7 +102,10 @@ class UserRepository extends BaseRepository public function disableUser(User $operator, $uid, $reason) { - $targetUser = User::query()->findOrFail(['id', 'username']); + $targetUser = User::query()->findOrFail($uid, ['id', 'enabled', 'username']); + if ($targetUser->enabled == User::ENABLED_NO) { + throw new NexusException('Already disabled!'); + } $banLog = [ 'uid' => $uid, 'username' => $targetUser->username, @@ -110,9 +114,34 @@ class UserRepository extends BaseRepository ]; $modCommentText = sprintf("Disable by %s, reason: %s.", $operator->username, $reason); DB::transaction(function () use ($targetUser, $banLog, $modCommentText) { - $targetUser->updateWithModComment(['enable' => User::ENABLED_NO], $modCommentText); + $targetUser->updateWithModComment(['enabled' => User::ENABLED_NO], $modCommentText); UserBanLog::query()->insert($banLog); }); + do_log("user: $uid, $modCommentText"); return true; } + + public function enableUser(User $operator, $uid) + { + $targetUser = User::query()->findOrFail($uid, ['id', 'enabled', 'username']); + if ($targetUser->enabled == User::ENABLED_YES) { + throw new NexusException('Already enabled!'); + } + $modCommentText = sprintf("Enable by %s.", $operator->username); + $targetUser->updateWithModComment(['enabled' => User::ENABLED_YES], $modCommentText); + do_log("user: $uid, $modCommentText"); + return true; + } + + public function getInviteInfo($id) + { + $user = User::query()->findOrFail($id, ['id']); + return $user->invitee_code()->with('inviter_user')->first(); + } + + public function getModComment($id) + { + $user = User::query()->findOrFail($id, ['modcomment']); + return $user->modcomment; + } } diff --git a/include/globalfunctions.php b/include/globalfunctions.php index 52c01d7d..52168c8b 100644 --- a/include/globalfunctions.php +++ b/include/globalfunctions.php @@ -451,7 +451,7 @@ function nexus_json_encode($data) function api(...$args) { - if (!isset($args[2])) { + if (func_num_args() < 3) { //参数少于3个时,默认为错误状态。 $ret = -1; $msg = isset($args[0]) ? $args[0] : 'ERROR'; diff --git a/routes/api.php b/routes/api.php index 3c12a781..2f5b2557 100644 --- a/routes/api.php +++ b/routes/api.php @@ -22,8 +22,12 @@ Route::group(['middleware' => ['auth:sanctum', 'permission', 'locale']], functio Route::resource('users', \App\Http\Controllers\UserController::class); Route::get('user-base', [\App\Http\Controllers\UserController::class, 'base']); Route::get('user-classes', [\App\Http\Controllers\UserController::class, 'classes']); + Route::get('user-invite-info', [\App\Http\Controllers\UserController::class, 'inviteInfo']); Route::get('user-match-exams', [\App\Http\Controllers\UserController::class, 'matchExams']); + Route::get('user-mod-comment', [\App\Http\Controllers\UserController::class, 'modComment']); Route::post('user-disable', [\App\Http\Controllers\UserController::class, 'disable']); + Route::post('user-enable', [\App\Http\Controllers\UserController::class, 'enable']); + Route::post('user-reset-password', [\App\Http\Controllers\UserController::class, 'resetPassword']); Route::resource('exams', \App\Http\Controllers\ExamController::class); Route::get('exam-indexes', [\App\Http\Controllers\ExamController::class, 'indexes']); @@ -32,6 +36,8 @@ Route::group(['middleware' => ['auth:sanctum', 'permission', 'locale']], functio Route::get('system-info', [\App\Http\Controllers\ToolController::class, 'systemInfo']); + Route::resource('settings', \App\Http\Controllers\SettingController::class); + }); Route::post('login', [\App\Http\Controllers\AuthenticateController::class, 'login']);