From a9f4318f382e4698891bb1e63da41a1a7d7d25f1 Mon Sep 17 00:00:00 2001 From: ricky rx Date: Tue, 23 Apr 2024 15:38:01 +0700 Subject: [PATCH] feat: user management page --- app/Http/Controllers/api/AuthController.php | 4 +- .../superadmin/UserManagementController.php | 31 ++++++ app/Models/User.php | 105 +++++++++++++++++- routes/api.php | 10 ++ 4 files changed, 145 insertions(+), 5 deletions(-) create mode 100644 app/Http/Controllers/api/superadmin/UserManagementController.php diff --git a/app/Http/Controllers/api/AuthController.php b/app/Http/Controllers/api/AuthController.php index bc85e23..6ffc2a2 100644 --- a/app/Http/Controllers/api/AuthController.php +++ b/app/Http/Controllers/api/AuthController.php @@ -41,7 +41,7 @@ class AuthController extends Controller { } public function changePassword(Request $request) { - $request->user()->changePassword($request); - return JSONResponse::Success(); + $request->merge(['id' => $request->user()->id]); + return User::changePasswordFromRequest($request); } } diff --git a/app/Http/Controllers/api/superadmin/UserManagementController.php b/app/Http/Controllers/api/superadmin/UserManagementController.php new file mode 100644 index 0000000..933a2e2 --- /dev/null +++ b/app/Http/Controllers/api/superadmin/UserManagementController.php @@ -0,0 +1,31 @@ +validate([ + 'perPage' => 'nullable|integer|min:1', + ...DatabaseHelper::getOrderBysValidations(), + 'search' => DatabaseHelper::getSearchValidation() + ]); + + $data = User::multiSearch($request->search, ['email', 'username']) + ->multiOrderBy($request->orderBys, 'created_at desc') + ->paginate($request->perPage ?? 10 ); + return JSONResponse::Success(['data' => $data]); + } + + public function save(Request $request) { return User::upsertFromRequest($request); } + public function update(Request $request) { return User::upsertFromRequest($request); } + public function changePassword(Request $request) { return User::changePasswordFromRequest($request); } + public function delete(Request $request) { return User::deleteFromRequest($request); } + + public function changeStatus(Request $request) { return User::changeStatusFromRequest($request); } +} diff --git a/app/Models/User.php b/app/Models/User.php index 9a2df29..b5392d3 100644 --- a/app/Models/User.php +++ b/app/Models/User.php @@ -5,16 +5,23 @@ namespace App\Models; // use Illuminate\Contracts\Auth\MustVerifyEmail; use App\Helper\FileHelper; +use App\Helper\JSONResponse; +use App\Helper\Traits\Models\CanMultiOrderBy; +use App\Helper\Traits\Models\CanMultiSearch; +use Illuminate\Database\Eloquent\Builder; use Illuminate\Database\Eloquent\Factories\HasFactory; use Illuminate\Foundation\Auth\User as Authenticatable; use Illuminate\Notifications\Notifiable; use Illuminate\Http\Request; +use Illuminate\Support\Facades\DB; use Illuminate\Support\Facades\Hash; use Laravel\Sanctum\HasApiTokens; class User extends Authenticatable { use HasApiTokens, HasFactory, Notifiable; + use CanMultiSearch; + use CanMultiOrderBy; /** * The attributes that are mass assignable. @@ -49,8 +56,98 @@ class User extends Authenticatable 'password' => 'hashed', ]; - public function changePassword(Request $request) { + public static function upsertFromRequest(Request $request) { $request->validate([ + 'id' => 'nullable|integer|exists:App\Models\User,id', + 'name' => 'required|string', + 'username' => 'required|string', + 'email' => 'required|email', + 'password' => 'required_without:id|string|min:8', + 'is_active' => 'required_with:id|in:true,false', + ], [ + 'password' => ['required_with' => 'The password field is required.'], + 'is_active' => ['required_with' => 'The is active field is required.'] + ]); + + try { + // try to upsert data + DB::beginTransaction(); + $user = null; + if(!$request->id) { + $user = new User(); + $user->is_active = $request->is_active; + $user->password = Hash::make($request->password); + } + else $user = User::findOrFail($request->id); + + $user->name = $request->email; + $user->email = $request->email; + $user->username = $request->username; + $user->checkUniqueFieldBeforeExecuteDB(); + $user->save(); + + // renew data; + DB::commit(); + return JSONResponse::Success(); + } catch (\Throwable $th) { + DB::rollBack(); + throw $th; + } + } + + public static function deleteFromRequest(Request $request) { + $request->validate(['id' => 'required|integer|exists:App\Models\User,id']); + try { + DB::beginTransaction(); + $user = User::findOrFail($request->id); + if($user->is_selected) throw new \Exception("Cannot delete video when 'is Selected' is true"); + + $oldDbFile = $user->file; + $user->delete(); + + if($oldDbFile) self::deleteFile($oldDbFile); + DB::commit(); + return JSONResponse::Success(); + } catch (\Throwable $th) { + DB::rollBack(); + throw $th; + } + + } + + public static function changeStatusFromRequest(Request $request) { + $request->validate(['id' => 'required|integer|exists:App\Models\User,id']); + + $user = User::findOrFail($request->id); + $user->preventChangeForSelfUser($request->user()); + $user->is_active = !$user->is_active; + $user->save(); + return JSONResponse::Success(); + } + + public function preventChangeForSelfUser(User $currentUser) { + if($currentUser->id == $this->id) throw new \Exception("You cannot 'delete' / 'change status' your own user"); + } + + public function checkUniqueFieldBeforeExecuteDB() { + // check email + $isUsernameExist = User::where('username', $this->username) + ->when($this->id, function(Builder $q, $userId) { + $q->where('id', '!=', $userId); + })->first(); + if($isUsernameExist) throw new \Exception("Username '" . $this->username . "' has already used by another user"); + + // check email + $isEmailExist = User::where('email', $this->email) + ->when($this->id, function(Builder $q, $userId) { + $q->where('id', '!=', $userId); + })->first(); + if($isEmailExist) throw new \Exception("Email '" . $this->email . "' has already used by another user"); + } + + public static function changePasswordFromRequest(Request $request) { + $request->validate([ + 'id' => 'required|integer|exists:App\Models\User,id', 'newPassword' => 'required|string|min:8', 'confirmNewPassword' => 'required|string|min:8', ]); @@ -59,8 +156,10 @@ class User extends Authenticatable throw new \Exception("New Password & Confirm New Pasword are not same"); } - $this->password = Hash::make($request->newPassword); - $this->save(); + $user = User::findOrFail($request->id); + $user->password = Hash::make($request->newPassword); + $user->save(); + return JSONResponse::Success(); } public function getObjSession($currentAccessToken) { diff --git a/routes/api.php b/routes/api.php index bcb0e71..ffc7e8e 100644 --- a/routes/api.php +++ b/routes/api.php @@ -2,6 +2,7 @@ use App\Http\Controllers\api\AuthController; use App\Http\Controllers\api\superadmin\ApkUploadController; +use App\Http\Controllers\api\superadmin\UserManagementController; use App\Http\Controllers\api\superadmin\VideoUploadController; use App\Http\Middleware\Cors; use App\Http\Middleware\UserAuthMiddleware; @@ -43,5 +44,14 @@ Route::middleware(USER_MIDDLEWARES)->prefix('superadmin')->group(function() { Route::post('/apk-upload/update', 'update'); Route::post('/apk-upload/delete', 'delete'); }); + + Route::controller(UserManagementController::class)->group(function() { + Route::post('/user-management', 'init'); + Route::post('/user-management/save', 'save'); + Route::post('/user-management/update', 'update'); + Route::post('/user-management/change-password', 'changePassword'); + Route::post('/user-management/change-status', 'changeStatus'); + Route::post('/user-management/delete', 'delete'); + }); }); // tmux session, tmux attach session -t \ No newline at end of file