commit ed9c8799dd1af124e55bc40e65d6c865f6fbad68 Author: daeng.deni@dharma.or.id Date: Sat May 27 23:06:00 2023 +0700 Initial Commit - API Authentication Module diff --git a/Config/.gitkeep b/Config/.gitkeep new file mode 100644 index 0000000..e69de29 diff --git a/Config/config.php b/Config/config.php new file mode 100644 index 0000000..897e117 --- /dev/null +++ b/Config/config.php @@ -0,0 +1,5 @@ + 'Auth' +]; diff --git a/Database/Migrations/.gitkeep b/Database/Migrations/.gitkeep new file mode 100644 index 0000000..e69de29 diff --git a/Database/Migrations/2014_10_12_000000_create_users_table.php b/Database/Migrations/2014_10_12_000000_create_users_table.php new file mode 100644 index 0000000..444fafb --- /dev/null +++ b/Database/Migrations/2014_10_12_000000_create_users_table.php @@ -0,0 +1,32 @@ +id(); + $table->string('name'); + $table->string('email')->unique(); + $table->timestamp('email_verified_at')->nullable(); + $table->string('password'); + $table->rememberToken(); + $table->timestamps(); + }); + } + + /** + * Reverse the migrations. + */ + public function down(): void + { + Schema::dropIfExists('users'); + } +}; diff --git a/Database/Migrations/2014_10_12_100000_create_password_reset_tokens_table.php b/Database/Migrations/2014_10_12_100000_create_password_reset_tokens_table.php new file mode 100644 index 0000000..81a7229 --- /dev/null +++ b/Database/Migrations/2014_10_12_100000_create_password_reset_tokens_table.php @@ -0,0 +1,28 @@ +string('email')->primary(); + $table->string('token'); + $table->timestamp('created_at')->nullable(); + }); + } + + /** + * Reverse the migrations. + */ + public function down(): void + { + Schema::dropIfExists('password_reset_tokens'); + } +}; diff --git a/Database/Migrations/2019_08_19_000000_create_failed_jobs_table.php b/Database/Migrations/2019_08_19_000000_create_failed_jobs_table.php new file mode 100644 index 0000000..249da81 --- /dev/null +++ b/Database/Migrations/2019_08_19_000000_create_failed_jobs_table.php @@ -0,0 +1,32 @@ +id(); + $table->string('uuid')->unique(); + $table->text('connection'); + $table->text('queue'); + $table->longText('payload'); + $table->longText('exception'); + $table->timestamp('failed_at')->useCurrent(); + }); + } + + /** + * Reverse the migrations. + */ + public function down(): void + { + Schema::dropIfExists('failed_jobs'); + } +}; diff --git a/Database/Migrations/2019_12_14_000001_create_personal_access_tokens_table.php b/Database/Migrations/2019_12_14_000001_create_personal_access_tokens_table.php new file mode 100644 index 0000000..e828ad8 --- /dev/null +++ b/Database/Migrations/2019_12_14_000001_create_personal_access_tokens_table.php @@ -0,0 +1,33 @@ +id(); + $table->morphs('tokenable'); + $table->string('name'); + $table->string('token', 64)->unique(); + $table->text('abilities')->nullable(); + $table->timestamp('last_used_at')->nullable(); + $table->timestamp('expires_at')->nullable(); + $table->timestamps(); + }); + } + + /** + * Reverse the migrations. + */ + public function down(): void + { + Schema::dropIfExists('personal_access_tokens'); + } +}; diff --git a/Database/Seeders/.gitkeep b/Database/Seeders/.gitkeep new file mode 100644 index 0000000..e69de29 diff --git a/Database/Seeders/AuthDatabaseSeeder.php b/Database/Seeders/AuthDatabaseSeeder.php new file mode 100644 index 0000000..c7a1fd0 --- /dev/null +++ b/Database/Seeders/AuthDatabaseSeeder.php @@ -0,0 +1,26 @@ +create(); + + // \App\Models\User::factory()->create([ + // 'name' => 'Test User', + // 'email' => 'test@example.com', + // ]); + + // $this->call("OthersTableSeeder"); + } +} diff --git a/Database/factories/UserFactory.php b/Database/factories/UserFactory.php new file mode 100644 index 0000000..a6ecc0a --- /dev/null +++ b/Database/factories/UserFactory.php @@ -0,0 +1,38 @@ + + */ +class UserFactory extends Factory +{ + /** + * Define the model's default state. + * + * @return array + */ + public function definition(): array + { + return [ + 'name' => fake()->name(), + 'email' => fake()->unique()->safeEmail(), + 'email_verified_at' => now(), + 'password' => '$2y$10$92IXUNpkjO0rOQ5byMi.Ye4oKoEa3Ro9llC/.og/at2.uheWG/igi', // password + 'remember_token' => Str::random(10), + ]; + } + + /** + * Indicate that the model's email address should be unverified. + */ + public function unverified(): static + { + return $this->state(fn (array $attributes) => [ + 'email_verified_at' => null, + ]); + } +} diff --git a/Entities/.gitkeep b/Entities/.gitkeep new file mode 100644 index 0000000..e69de29 diff --git a/Entities/User.php b/Entities/User.php new file mode 100644 index 0000000..23ff675 --- /dev/null +++ b/Entities/User.php @@ -0,0 +1,45 @@ + + */ + protected $fillable = [ + 'name', + 'email', + 'password', + ]; + + /** + * The attributes that should be hidden for serialization. + * + * @var array + */ + protected $hidden = [ + 'password', + 'remember_token', + ]; + + /** + * The attributes that should be cast. + * + * @var array + */ + protected $casts = [ + 'email_verified_at' => 'datetime', + 'password' => 'hashed', + ]; +} diff --git a/Http/Controllers/.gitkeep b/Http/Controllers/.gitkeep new file mode 100644 index 0000000..e69de29 diff --git a/Http/Controllers/AuthController.php b/Http/Controllers/AuthController.php new file mode 100644 index 0000000..182bf01 --- /dev/null +++ b/Http/Controllers/AuthController.php @@ -0,0 +1,60 @@ +all(), [ + 'name' => 'required', + 'email' => 'required|email', + 'password' => 'required', + 'password_confirmation' => 'required|same:password', + ]); + + if ($validator->fails()) { + return $this->sendError('Validation Error.', $validator->errors()); + } + + $input = $request->all(); + $input['password'] = bcrypt($input['password']); + $user = User::create($input); + $success['token'] = $user->createToken('IAT')->plainTextToken; + $success['name'] = $user->name; + + return $this->sendResponse($success, 'User register successfully.'); + } + + /** + * Login api + * + * @return \Illuminate\Http\Response + */ + public function login(Request $request) + : JsonResponse + { + if (Auth::attempt(['email' => $request->email, 'password' => $request->password])) { + $user = Auth::user(); + $success['token'] = $user->createToken('MyApp')->plainTextToken; + $success['name'] = $user->name; + + return $this->sendResponse($success, 'User login successfully.'); + } else { + return $this->sendError('Unauthorised.', ['error' => 'Unauthorised']); + } + } + } diff --git a/Http/Controllers/UserController.php b/Http/Controllers/UserController.php new file mode 100644 index 0000000..9f24c6d --- /dev/null +++ b/Http/Controllers/UserController.php @@ -0,0 +1,119 @@ +sendResponse($users, 'Users retrieved successfully.'); + } + + /** + * Store a newly created resource in storage. + * + * @param Request $request + * + * @return Renderable + */ + public function store(Request $request) + : JsonResponse + { + $validator = Validator::make($request->all(), [ + 'name' => 'required', + 'email' => 'required|email', + 'password' => 'required', + 'password_confirmation' => 'required|same:password', + ]); + + if ($validator->fails()) { + return $this->sendError('Validation Error.', $validator->errors()); + } + + $input = $request->all(); + $input['password'] = bcrypt($input['password']); + $user = User::create($input); + + return $this->sendResponse($user, 'User create successfully.'); + + } + + /** + * Show the specified resource. + * + * @param int $id + * + * @return Renderable + */ + public function show($id) : JsonResponse + { + $user = User::find($id); + if (is_null($user)) { + return $this->sendError('User not found.',404); + } + + return $this->sendResponse($user, 'User retrieved successfully.'); + } + + /** + * Update the specified resource in storage. + * + * @param Request $request + * @param int $id + * + * @return Renderable + */ + public function update(Request $request, User $user): JsonResponse + + { + $validator = Validator::make($request->all(), [ + 'name' => 'required', + 'email' => 'required|email', + 'password' => 'required', + 'password_confirmation' => 'required|same:password', + ]); + + if ($validator->fails()) { + return $this->sendError('Validation Error.', $validator->errors()); + } + + $input = $request->all(); + $input['password'] = bcrypt($input['password']); + $user->update($input); + + return $this->sendResponse($user, 'User update successfully.'); + } + + /** + * Remove the specified resource from storage. + * + * @param int $id + * + * @return Renderable + */ + public function destroy($id) + { + $user = User::find($id); + if (is_null($user)) { + return $this->sendError('User not found.',404); + } + + $user->delete(); + + return $this->sendResponse($user, 'User deleted successfully.'); + } + } diff --git a/Http/Requests/.gitkeep b/Http/Requests/.gitkeep new file mode 100644 index 0000000..e69de29 diff --git a/Providers/.gitkeep b/Providers/.gitkeep new file mode 100644 index 0000000..e69de29 diff --git a/Providers/AuthServiceProvider.php b/Providers/AuthServiceProvider.php new file mode 100644 index 0000000..1a6dd8a --- /dev/null +++ b/Providers/AuthServiceProvider.php @@ -0,0 +1,114 @@ +registerTranslations(); + $this->registerConfig(); + $this->registerViews(); + $this->loadMigrationsFrom(module_path($this->moduleName, 'Database/Migrations')); + } + + /** + * Register the service provider. + * + * @return void + */ + public function register() + { + $this->app->register(RouteServiceProvider::class); + } + + /** + * Register config. + * + * @return void + */ + protected function registerConfig() + { + $this->publishes([ + module_path($this->moduleName, 'Config/config.php') => config_path($this->moduleNameLower . '.php'), + ], 'config'); + $this->mergeConfigFrom( + module_path($this->moduleName, 'Config/config.php'), $this->moduleNameLower + ); + } + + /** + * Register views. + * + * @return void + */ + public function registerViews() + { + $viewPath = resource_path('views/modules/' . $this->moduleNameLower); + + $sourcePath = module_path($this->moduleName, 'Resources/views'); + + $this->publishes([ + $sourcePath => $viewPath + ], ['views', $this->moduleNameLower . '-module-views']); + + $this->loadViewsFrom(array_merge($this->getPublishableViewPaths(), [$sourcePath]), $this->moduleNameLower); + } + + /** + * Register translations. + * + * @return void + */ + public function registerTranslations() + { + $langPath = resource_path('lang/modules/' . $this->moduleNameLower); + + if (is_dir($langPath)) { + $this->loadTranslationsFrom($langPath, $this->moduleNameLower); + $this->loadJsonTranslationsFrom($langPath); + } else { + $this->loadTranslationsFrom(module_path($this->moduleName, 'Resources/lang'), $this->moduleNameLower); + $this->loadJsonTranslationsFrom(module_path($this->moduleName, 'Resources/lang')); + } + } + + /** + * Get the services provided by the provider. + * + * @return array + */ + public function provides() + { + return []; + } + + private function getPublishableViewPaths(): array + { + $paths = []; + foreach (\Config::get('view.paths') as $path) { + if (is_dir($path . '/modules/' . $this->moduleNameLower)) { + $paths[] = $path . '/modules/' . $this->moduleNameLower; + } + } + return $paths; + } +} diff --git a/Providers/RouteServiceProvider.php b/Providers/RouteServiceProvider.php new file mode 100644 index 0000000..2efa665 --- /dev/null +++ b/Providers/RouteServiceProvider.php @@ -0,0 +1,69 @@ +mapApiRoutes(); + + $this->mapWebRoutes(); + } + + /** + * Define the "web" routes for the application. + * + * These routes all receive session state, CSRF protection, etc. + * + * @return void + */ + protected function mapWebRoutes() + { + Route::middleware('web') + ->namespace($this->moduleNamespace) + ->group(module_path('Auth', '/Routes/web.php')); + } + + /** + * Define the "api" routes for the application. + * + * These routes are typically stateless. + * + * @return void + */ + protected function mapApiRoutes() + { + Route::prefix('api') + ->middleware('api') + ->namespace($this->moduleNamespace) + ->group(module_path('Auth', '/Routes/api.php')); + } +} diff --git a/Routes/.gitkeep b/Routes/.gitkeep new file mode 100644 index 0000000..e69de29 diff --git a/Routes/api.php b/Routes/api.php new file mode 100644 index 0000000..6eee537 --- /dev/null +++ b/Routes/api.php @@ -0,0 +1,24 @@ +group(function () { + Route::post('register', 'register'); + Route::post('login', 'login'); + }); + + Route::middleware('auth:sanctum')->group(function () { + Route::resource('user', UserController::class); + }); diff --git a/Routes/web.php b/Routes/web.php new file mode 100644 index 0000000..cb3f0ca --- /dev/null +++ b/Routes/web.php @@ -0,0 +1,12 @@ +