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 @@
+
+
+
+ Yes
+ No
+
+ Enable backup or not.
+
+
+
+
+ Daily
+ Hourly
+
+ Backup Frequency.
+
+
+
+
+
+
+
+
+
+ Do backup at this hour, If frequency = 'hourly', this value will be ignore.
+
+
+
+
+
+
+
+
+
+ Do backup at this minute.
+
+
+
+
+
+
+ Google drive client ID.
+
+
+
+
+
+
+ Google drive client secret.
+
+
+
+
+
+
+ Google drive refresh token.
+
+
+
+
+
+
+ Google drive folder ID. If not set, will store in root.
+
+
+
+
+ Submit
+
+
+
+
+
+
+
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 @@
-
+
+
-
- Smtp
@@ -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}} |
+
+
+ Disable
+
+
+
+
+ Enable
+
+
+
+ |
+
| 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']);