Login, Register, Forgot Password and List

This commit is contained in:
Supan Adit Pratama 2020-07-01 21:06:40 +07:00
parent b2e434483d
commit d4c089fb22
11 changed files with 663 additions and 114 deletions

View File

@ -0,0 +1,137 @@
<?php
namespace App\Http\Controllers;
use App\Mail\ForgotPassword;
use Illuminate\Http\Request;
use Illuminate\Support\Facades\Hash;
use Illuminate\Support\Facades\Mail;
use Illuminate\Support\Str;
class SecurityController extends Controller
{
public function formLogin(Request $request)
{
$request->validate([
"email" => "required",
"password" => "required",
]);
$user = \App\User::where("email", $request->input("email"))->first();
if ($user != null) {
if (Hash::check($request->input('password'), $user->password)) {
// Set Session
$request->session()->put('user', $user->id);
$request->session()->put('name', $user->name);
$request->session()->put('email', $user->email);
return response()->json([
"message" => "Success login",
], 200);
} else {
return response()->json([
"message" => "Username or password is wrong",
], 401);
}
} else {
return response()->json([
"message" => "Username or password is wrong",
], 401);
}
}
public function formRegister(Request $request)
{
$request->validate([
"name" => "required",
"email" => "required",
"password" => "required",
"password_confirm" => "required",
]);
$user = \App\User::where("email", $request->input("email"))->first();
if ($user != null) {
return response()->json([
"message" => "User with email " . $user->email . " is exist",
], 400);
} else {
if ($request->input('password') != $request->input('password_confirm')) {
return response()->json([
"message" => "Confirm password is different with provided password",
], 400);
} else {
$user = new \App\User();
$user->name = $request->input("name");
$user->email = $request->input("email");
$user->password = Hash::make($request->input("password"));
if ($user->save()) {
return response()->json([
"message" => "Register success, now you can login...",
], 200);
} else {
return response()->json([
"message" => "Failed to register new user",
], 400);
}
}
}
}
public function formForgotPassword(Request $request)
{
$request->validate([
"email" => "required",
]);
$user = \App\User::where("email", $request->input("email"))->first();
if ($user != null) {
$name = $user->name;
$newPassword = Str::random(4);
$user->password = Hash::make($newPassword);
Mail::to($user->email)->send(new ForgotPassword($name, $newPassword));
$user->save();
return response()->json([
"message" => "Please check your email",
], 200);
} else {
return response()->json([
"message" => "Email " . $request->input('email') . " is not exist",
], 400);
}
}
public function formChangePassword(Request $request)
{
$request->validate([
"email" => "required",
"password" => "required",
"password_confirm" => "required",
]);
$user = \App\User::where("email", $request->input("email"))->first();
if ($user != null) {
if ($request->input('password') == $request->input('password_confirm')) {
$user->password = Hash::make($request->input('password'));
if ($user->save()) {
return response()->json([
"message" => "Success change password",
], 200);
} else {
return response()->json([
"message" => "Failed change password",
], 400);
}
} else {
return response()->json([
"message" => "New password is not match with confirm password",
], 400);
}
} else {
return response()->json([
"message" => "Email " . $request->input('email') . " is not exist",
], 400);
}
}
public function formLogout(Request $request)
{
$request->session()->flush();
return redirect('/');
}
}

View File

@ -12,6 +12,16 @@ use Illuminate\Support\Str;
class UrlShortenerController extends Controller class UrlShortenerController extends Controller
{ {
public function viewHome()
{
return view('home');
}
public function viewList()
{
return view('list');
}
public function createShortURL(Request $request) public function createShortURL(Request $request)
{ {
$request->validate([ $request->validate([
@ -44,6 +54,10 @@ class UrlShortenerController extends Controller
$saveGeneratedURL = new UrlAddress(); $saveGeneratedURL = new UrlAddress();
$saveGeneratedURL['url_destination'] = $request->input('url'); $saveGeneratedURL['url_destination'] = $request->input('url');
$saveGeneratedURL['path_generated'] = $generatedPath; $saveGeneratedURL['path_generated'] = $generatedPath;
$userSession = $request->session()->has('user');
if ($userSession) {
$saveGeneratedURL['user_id'] = $userSession;
}
if ($request->input('expired_date') != null && $request->input('expired_date') != "") { if ($request->input('expired_date') != null && $request->input('expired_date') != "") {
$saveGeneratedURL['date_expired'] = $request->input('expired_date'); $saveGeneratedURL['date_expired'] = $request->input('expired_date');

View File

@ -55,6 +55,7 @@ class Kernel extends HttpKernel
*/ */
protected $routeMiddleware = [ protected $routeMiddleware = [
'auth' => \App\Http\Middleware\Authenticate::class, 'auth' => \App\Http\Middleware\Authenticate::class,
'auth.web' => \App\Http\Middleware\WebAuthenticate::class,
'auth.basic' => \Illuminate\Auth\Middleware\AuthenticateWithBasicAuth::class, 'auth.basic' => \Illuminate\Auth\Middleware\AuthenticateWithBasicAuth::class,
'bindings' => \Illuminate\Routing\Middleware\SubstituteBindings::class, 'bindings' => \Illuminate\Routing\Middleware\SubstituteBindings::class,
'cache.headers' => \Illuminate\Http\Middleware\SetCacheHeaders::class, 'cache.headers' => \Illuminate\Http\Middleware\SetCacheHeaders::class,

View File

@ -0,0 +1,24 @@
<?php
namespace App\Http\Middleware;
use Closure;
class WebAuthenticate
{
/**
* Handle an incoming request.
*
* @param \Illuminate\Http\Request $request
* @param \Closure $next
* @return mixed
*/
public function handle($request, Closure $next)
{
if ($request->session()->has('user')) {
return $next($request);
} else {
return redirect('login');
}
}
}

View File

@ -0,0 +1,37 @@
<?php
namespace App\Mail;
use Illuminate\Bus\Queueable;
use Illuminate\Mail\Mailable;
use Illuminate\Queue\SerializesModels;
class ForgotPassword extends Mailable
{
use Queueable, SerializesModels;
public $name;
public $password;
/**
* Create a new message instance.
*
* @param $name
* @param $password
*/
public function __construct($name, $password)
{
$this->name = $name;
$this->password = $password;
}
/**
* Build the message.
*
* @return $this
*/
public function build()
{
return $this->view('template.email.forgot');
}
}

View File

@ -21,7 +21,7 @@ use Illuminate\Support\Str;
$factory->define(User::class, function (Faker $faker) { $factory->define(User::class, function (Faker $faker) {
return [ return [
'name' => $faker->name, 'name' => $faker->name,
'email' => "admin@admin.com", 'email' => "admin@email.com",
'email_verified_at' => now(), 'email_verified_at' => now(),
'password' => Hash::make('123'), // password 'password' => Hash::make('123'), // password
'remember_token' => Str::random(10), 'remember_token' => Str::random(10),

View File

@ -64,20 +64,40 @@
<div class="collapse navbar-collapse pull-left" id="navbar-collapse"> <div class="collapse navbar-collapse pull-left" id="navbar-collapse">
<ul class="nav navbar-nav"> <ul class="nav navbar-nav">
<li><a href="/">Home</a></li> <li><a href="/">Home</a></li>
@if(Session::get('user') != null)
<li>
<a href="/list">
My URL Shortener
</a>
</li>
<li>
<a href="#" data-toggle="modal" data-target="#change-password-modal">
Change Password
</a>
</li>
@endif
</ul> </ul>
</div> </div>
<div class="navbar-custom-menu"> <div class="navbar-custom-menu">
<ul class="nav navbar-nav"> <ul class="nav navbar-nav">
<li> @if(Session::get('user') == null)
<a href="#"> <li>
<span>Sign In</span> <a href="#" data-toggle="modal" data-target="#sign-in-modal">
</a> <span>Sign In</span>
</li> </a>
<li> </li>
<a href="#"> <li>
<span>Register</span> <a href="#" data-toggle="modal" data-target="#register-modal">
</a> <span>Register</span>
</li> </a>
</li>
@else
<li>
<a href="#" class="logout-button">
<span>Sign Out</span>
</a>
</li>
@endif
</ul> </ul>
</div> </div>
</div> </div>
@ -120,6 +140,152 @@
</div> </div>
<!-- ./wrapper --> <!-- ./wrapper -->
@if(Session::get('user') == null)
{{-- Forgot Password Modal --}}
<div class="modal fade" id="forgot-password-modal">
<div class="modal-dialog">
<div class="modal-content">
<form action="/" method="post" id="forgot-password-modal-form">
<div class="modal-header">
<h4 class="modal-title">Forgot Password</h4>
</div>
<div class="modal-body">
<div class="form-group">
<label>Email</label>
<input type="email" class="form-control" placeholder="Insert your email"
id="forgot-password-modal-form-field-email">
</div>
</div>
<div class="modal-footer">
<button type="button" class="btn btn-default pull-left" data-dismiss="modal">Cancel</button>
<button type="submit" class="btn btn-primary">
<i class="fa fa-spinner fa-spin" id="forgot-password-modal-save-loading-indicator"></i>
<span id="forgot-password-modal-save-button-label">Sign In</span>
</button>
</div>
</form>
</div>
<!-- /.modal-content -->
</div>
<!-- /.modal-dialog -->
</div>
{{-- Sign In Modal --}}
<div class="modal fade" id="sign-in-modal">
<div class="modal-dialog">
<div class="modal-content">
<form action="/" method="post" id="sign-in-modal-form">
<div class="modal-header">
<h4 class="modal-title">Sign In</h4>
</div>
<div class="modal-body">
<div class="form-group">
<label>Email</label>
<input type="email" class="form-control" placeholder="Insert your email"
id="sign-in-modal-form-field-email">
</div>
<div class="form-group">
<label>Password</label>
<input type="password" class="form-control" placeholder="Insert your new password"
id="sign-in-modal-form-field-password">
</div>
</div>
<div class="modal-footer">
<button type="button" class="btn btn-default pull-left" data-dismiss="modal">Cancel</button>
<button type="button" class="btn btn-danger pull-left"
id="sign-in-modal-button-forgot-password">Forgot Password
</button>
<button type="submit" class="btn btn-primary">
<i class="fa fa-spinner fa-spin" id="sign-in-modal-save-loading-indicator"></i>
<span id="sign-in-modal-save-button-label">Sign In</span>
</button>
</div>
</form>
</div>
<!-- /.modal-content -->
</div>
<!-- /.modal-dialog -->
</div>
{{-- Register Modal --}}
<div class="modal fade" id="register-modal">
<div class="modal-dialog">
<div class="modal-content">
<form action="/" method="post" id="register-modal-form">
<div class="modal-header">
<h4 class="modal-title">Register</h4>
</div>
<div class="modal-body">
<div class="form-group">
<label>Name</label>
<input type="text" class="form-control" placeholder="Insert your name"
id="register-modal-form-field-name">
</div>
<div class="form-group">
<label>Email</label>
<input type="email" class="form-control" placeholder="Insert your email"
id="register-modal-form-field-email">
</div>
<div class="form-group">
<label>Password</label>
<input type="password" class="form-control" placeholder="Insert your new password"
id="register-modal-form-field-password">
</div>
<div class="form-group">
<label>Confirm Password</label>
<input type="password" class="form-control" placeholder="Please confirm new password"
id="register-modal-form-password-confirm">
</div>
</div>
<div class="modal-footer">
<button type="button" class="btn btn-default pull-left" data-dismiss="modal">Cancel</button>
<button type="submit" class="btn btn-primary">
<i class="fa fa-spinner fa-spin" id="register-modal-save-loading-indicator"></i>
<span id="register-modal-save-button-label">Register</span>
</button>
</div>
</form>
</div>
<!-- /.modal-content -->
</div>
<!-- /.modal-dialog -->
</div>
@else
{{-- Change Password Modal --}}
<div class="modal fade" id="change-password-modal">
<div class="modal-dialog">
<div class="modal-content">
<form action="/" method="post" id="change-password-modal-form">
<div class="modal-header">
<h4 class="modal-title">Change Password</h4>
</div>
<div class="modal-body">
<div class="form-group">
<label>New Password</label>
<input type="password" class="form-control" placeholder="Insert your new password"
id="change-password-modal-form-field-password">
</div>
<div class="form-group">
<label>Confirm Password</label>
<input type="password" class="form-control" placeholder="Please confirm new password"
id="change-password-modal-form-field-password-confirm">
</div>
</div>
<div class="modal-footer">
<button type="button" class="btn btn-default pull-left" data-dismiss="modal">Cancel</button>
<button type="submit" class="btn btn-primary">
<i class="fa fa-spinner fa-spin" id="change-password-modal-save-loading-indicator"></i>
<span id="change-password-modal-save-button-label">Save</span>
</button>
</div>
</form>
</div>
<!-- /.modal-content -->
</div>
<!-- /.modal-dialog -->
</div>
@endif
<!-- jQuery 3 --> <!-- jQuery 3 -->
<script src="{{asset('vendor/jquery/dist/jquery.min.js')}}"></script> <script src="{{asset('vendor/jquery/dist/jquery.min.js')}}"></script>
<!-- Bootstrap 3.3.7 --> <!-- Bootstrap 3.3.7 -->
@ -143,6 +309,177 @@
<!-- AdminLTE for demo purposes --> <!-- AdminLTE for demo purposes -->
<script src="{{asset('dist/js/demo.js')}}"></script> <script src="{{asset('dist/js/demo.js')}}"></script>
<script type="application/javascript">
$(document).ready(function () {
@if(Session::get('user') == null)
$("#forgot-password-modal-save-loading-indicator").hide();
$("#sign-in-modal-save-loading-indicator").hide();
$("#register-modal-save-loading-indicator").hide();
$("#sign-in-modal-button-forgot-password").click(function () {
$("#sign-in-modal").modal('hide');
$("#forgot-password-modal").modal('show');
});
$("#forgot-password-modal-form").on("submit", function (e) {
e.preventDefault();
$.ajax({
type: "POST",
url: "/web/forgot",
headers: {
'X-CSRF-TOKEN': $('meta[name="csrf-token"]').attr('content'),
},
data: JSON.stringify({
"email": $("#forgot-password-modal-form-field-email").val(),
}),
contentType: "application/json",
dataType: "json",
async: true,
beforeSend: function () {
$("#forgot-password-modal-save-loading-indicator").show();
$("#forgot-password-modal-save-button-label").hide();
},
success: function (result) {
$("#forgot-password-modal").modal('hide');
toastr.success(result.message);
},
error: function (result) {
toastr.error(result.responseJSON.message);
},
complete: function () {
$("#forgot-password-modal-save-loading-indicator").hide();
$("#forgot-password-modal-save-button-label").show();
}
});
});
$("#sign-in-modal-form").on("submit", function (e) {
e.preventDefault();
$.ajax({
type: "POST",
url: "/web/login",
headers: {
'X-CSRF-TOKEN': $('meta[name="csrf-token"]').attr('content'),
},
data: JSON.stringify({
"email": $("#sign-in-modal-form-field-email").val(),
"password": $("#sign-in-modal-form-field-password").val()
}),
contentType: "application/json",
dataType: "json",
async: true,
beforeSend: function () {
$("#sign-in-modal-save-loading-indicator").show();
$("#sign-in-modal-save-button-label").hide();
},
success: function (result) {
toastr.success(result.message);
$("#sign-in-modal-save-loading-indicator").hide();
$("#sign-in-modal-save-button-label").hide();
window.setTimeout(function () {
location.reload();
}, 500);
},
error: function (result) {
toastr.error(result.responseJSON.message);
},
complete: function () {
$("#sign-in-modal-save-loading-indicator").hide();
$("#sign-in-modal-save-button-label").show();
}
});
});
$("#register-modal-form").on("submit", function (e) {
e.preventDefault();
$.ajax({
type: "POST",
url: "/web/register",
headers: {
'X-CSRF-TOKEN': $('meta[name="csrf-token"]').attr('content'),
},
data: JSON.stringify({
"email": $("#register-modal-form-field-email").val(),
"password": $("#register-modal-form-field-password").val(),
"name": $("#register-modal-form-field-password").val(),
"password_confirm": $("#register-modal-form-password-confirm").val(),
}),
contentType: "application/json",
dataType: "json",
async: true,
beforeSend: function () {
$("#register-modal-save-loading-indicator").show();
$("#register-modal-save-button-label").hide();
},
success: function (result) {
toastr.success(result.message);
},
error: function (result) {
toastr.error(result.responseJSON.message);
},
complete: function () {
$("#register-modal-save-loading-indicator").hide();
$("#register-modal-save-button-label").show();
$("#register-modal").modal('hide');
}
});
});
@else
$("#change-password-modal-save-loading-indicator").hide();
$(".logout-button").on('click', function () {
swal({
title: "Do you want to sign out ?",
buttons: ["No", "Yes"],
dangerMode: true,
}).then((logout) => {
if (logout) {
location.href = "/web/logout";
}
});
});
$("#change-password-modal-form").on("submit", function (e) {
e.preventDefault();
const password = $("#change-password-modal-form-field-password");
const passwordConfirm = $("#change-password-modal-form-field-password-confirm");
$.ajax({
type: "POST",
url: "/web/change/password",
headers: {
'X-CSRF-TOKEN': $('meta[name="csrf-token"]').attr('content'),
},
data: JSON.stringify({
"email": "{{ Session::get('email') }}",
"password": password.val(),
"password_confirm": passwordConfirm.val(),
}),
contentType: "application/json",
dataType: "json",
async: true,
beforeSend: function () {
$("#change-password-modal-save-loading-indicator").show();
$("#change-password-modal-save-button-label").hide();
},
success: function (result) {
password.val(null);
passwordConfirm.val(null);
toastr.success(result.message);
$("#change-password-modal").modal('hide');
},
error: function (result) {
toastr.error(result.responseJSON.message);
},
complete: function () {
$("#change-password-modal-save-loading-indicator").hide();
$("#change-password-modal-save-button-label").show();
}
});
});
@endif
});
</script>
@yield('js') @yield('js')
</body> </body>

84
resources/views/list.blade.php Executable file
View File

@ -0,0 +1,84 @@
@extends('layout.default')
@section('title')
My URL Shortener
@endsection
@section('subtitle')
List all of your own URL Shortener
@endsection
@section('content')
<div class="box box-info">
<div class="box-header">
<h3 class="box-title">
<input type="text" class="form-control" placeholder="Search"/>
</h3>
<div class="box-tools">
<ul class="pagination pagination-sm no-margin pull-right">
<li><a href="#">&laquo;</a></li>
<li><a href="#">1</a></li>
<li><a href="#">2</a></li>
<li><a href="#">3</a></li>
<li><a href="#">&raquo;</a></li>
</ul>
</div>
</div>
<!-- /.box-header -->
<div class="box-body">
<table class="table table-condensed">
<tr>
<th style="width: 10px">#</th>
<th>Destination</th>
<th>Short URL</th>
<th>Click</th>
<th>Expired Date</th>
<th>Protection</th>
<th style="width: 40px">Action</th>
</tr>
<tr>
<td>1.</td>
<td>http://www.google.com</td>
<td>http://shorturl.test/s/sasd</td>
<td>5</td>
<td><label class="label label-success">2020-02-20</label></td>
<td><label class="label label-danger">No</label></td>
<td>
<button class="btn btn-danger btn-xs"><i class="fa fa-trash"></i></button>
</td>
</tr>
<tr>
<td>1.</td>
<td>http://www.google.com</td>
<td>http://shorturl.test/s/ssdf</td>
<td>5</td>
<td><label class="label label-danger">2020-02-20</label></td>
<td><label class="label label-success">Yes</label></td>
<td>
<button class="btn btn-danger btn-xs"><i class="fa fa-trash"></i></button>
</td>
</tr>
</table>
</div>
<!-- /.box-body -->
</div>
<!-- /.box -->
@endsection
@section('js')
<script type="application/javascript">
const clipboard = new ClipboardJS('.btn');
$(document).ready(function () {
clipboard.on('success', function (e) {
toastr.success("URL Copied");
e.clearSelection();
});
clipboard.on('error', function (e) {
toastr.failed("Cannot Copy URL");
});
});
</script>
@endsection

View File

@ -0,0 +1,5 @@
<div>
Hi, {{ $name }}<br/>
This is your new password : {{ $password }}<br/>
<p>You can change it after login back</p>
</div>

View File

@ -1,100 +0,0 @@
<!DOCTYPE html>
<html lang="{{ str_replace('_', '-', app()->getLocale()) }}">
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1">
<title>Laravel</title>
<!-- Fonts -->
<link href="https://fonts.googleapis.com/css?family=Nunito:200,600" rel="stylesheet">
<!-- Styles -->
<style>
html, body {
background-color: #fff;
color: #636b6f;
font-family: 'Nunito', sans-serif;
font-weight: 200;
height: 100vh;
margin: 0;
}
.full-height {
height: 100vh;
}
.flex-center {
align-items: center;
display: flex;
justify-content: center;
}
.position-ref {
position: relative;
}
.top-right {
position: absolute;
right: 10px;
top: 18px;
}
.content {
text-align: center;
}
.title {
font-size: 84px;
}
.links > a {
color: #636b6f;
padding: 0 25px;
font-size: 13px;
font-weight: 600;
letter-spacing: .1rem;
text-decoration: none;
text-transform: uppercase;
}
.m-b-md {
margin-bottom: 30px;
}
</style>
</head>
<body>
<div class="flex-center position-ref full-height">
@if (Route::has('login'))
<div class="top-right links">
@auth
<a href="{{ url('/home') }}">Home</a>
@else
<a href="{{ route('login') }}">Login</a>
@if (Route::has('register'))
<a href="{{ route('register') }}">Register</a>
@endif
@endauth
</div>
@endif
<div class="content">
<div class="title m-b-md">
Laravel
</div>
<div class="links">
<a href="https://laravel.com/docs">Docs</a>
<a href="https://laracasts.com">Laracasts</a>
<a href="https://laravel-news.com">News</a>
<a href="https://blog.laravel.com">Blog</a>
<a href="https://nova.laravel.com">Nova</a>
<a href="https://forge.laravel.com">Forge</a>
<a href="https://vapor.laravel.com">Vapor</a>
<a href="https://github.com/laravel/laravel">GitHub</a>
</div>
</div>
</div>
</body>
</html>

View File

@ -13,9 +13,19 @@ use Illuminate\Support\Facades\Route;
| |
*/ */
Route::get('/', function () { Route::get('/', "UrlShortenerController@viewHome");
return view('home');
});
Route::post('/s/g', "UrlShortenerController@createShortURL"); Route::post('/s/g', "UrlShortenerController@createShortURL");
Route::get('/s/{pathGenerated}', "UrlShortenerController@openDestination"); Route::get('/s/{pathGenerated}', "UrlShortenerController@openDestination");
Route::post('/s/{pathGenerated}/open/protection', "UrlShortenerController@openProtectedDestination"); Route::post('/s/{pathGenerated}/open/protection', "UrlShortenerController@openProtectedDestination");
Route::middleware('auth.web')->group(function () {
Route::get('/list', "UrlShortenerController@viewList");
Route::post('/web/change/password', "SecurityController@formChangePassword");
});
Route::post('/web/login', "SecurityController@formLogin");
Route::post('/web/forgot', "SecurityController@formForgotPassword");
Route::post('/web/register', "SecurityController@formRegister");
Route::get('/web/logout', "SecurityController@formLogout");