Add users, teams, authentication, profile/login/register/navbar views

This commit is contained in:
Andras Bacsai
2023-03-24 14:54:17 +01:00
parent 436d22e385
commit e47d493776
36 changed files with 1106 additions and 66 deletions

View File

@@ -0,0 +1,40 @@
<?php
namespace App\Actions\Fortify;
use App\Models\User;
use Illuminate\Support\Facades\Hash;
use Illuminate\Support\Facades\Validator;
use Illuminate\Validation\Rule;
use Laravel\Fortify\Contracts\CreatesNewUsers;
class CreateNewUser implements CreatesNewUsers
{
use PasswordValidationRules;
/**
* Validate and create a newly registered user.
*
* @param array<string, string> $input
*/
public function create(array $input): User
{
Validator::make($input, [
'name' => ['required', 'string', 'max:255'],
'email' => [
'required',
'string',
'email',
'max:255',
Rule::unique(User::class),
],
'password' => $this->passwordRules(),
])->validate();
return User::create([
'name' => $input['name'],
'email' => $input['email'],
'password' => Hash::make($input['password']),
]);
}
}

View File

@@ -0,0 +1,18 @@
<?php
namespace App\Actions\Fortify;
use Laravel\Fortify\Rules\Password;
trait PasswordValidationRules
{
/**
* Get the validation rules used to validate passwords.
*
* @return array<int, \Illuminate\Contracts\Validation\Rule|array|string>
*/
protected function passwordRules(): array
{
return ['required', 'string', new Password, 'confirmed'];
}
}

View File

@@ -0,0 +1,29 @@
<?php
namespace App\Actions\Fortify;
use App\Models\User;
use Illuminate\Support\Facades\Hash;
use Illuminate\Support\Facades\Validator;
use Laravel\Fortify\Contracts\ResetsUserPasswords;
class ResetUserPassword implements ResetsUserPasswords
{
use PasswordValidationRules;
/**
* Validate and reset the user's forgotten password.
*
* @param array<string, string> $input
*/
public function reset(User $user, array $input): void
{
Validator::make($input, [
'password' => $this->passwordRules(),
])->validate();
$user->forceFill([
'password' => Hash::make($input['password']),
])->save();
}
}

View File

@@ -0,0 +1,32 @@
<?php
namespace App\Actions\Fortify;
use App\Models\User;
use Illuminate\Support\Facades\Hash;
use Illuminate\Support\Facades\Validator;
use Laravel\Fortify\Contracts\UpdatesUserPasswords;
class UpdateUserPassword implements UpdatesUserPasswords
{
use PasswordValidationRules;
/**
* Validate and update the user's password.
*
* @param array<string, string> $input
*/
public function update(User $user, array $input): void
{
Validator::make($input, [
'current_password' => ['required', 'string', 'current_password:web'],
'password' => $this->passwordRules(),
], [
'current_password.current_password' => __('The provided password does not match your current password.'),
])->validateWithBag('updatePassword');
$user->forceFill([
'password' => Hash::make($input['password']),
])->save();
}
}

View File

@@ -0,0 +1,58 @@
<?php
namespace App\Actions\Fortify;
use App\Models\User;
use Illuminate\Contracts\Auth\MustVerifyEmail;
use Illuminate\Support\Facades\Validator;
use Illuminate\Validation\Rule;
use Laravel\Fortify\Contracts\UpdatesUserProfileInformation;
class UpdateUserProfileInformation implements UpdatesUserProfileInformation
{
/**
* Validate and update the given user's profile information.
*
* @param array<string, string> $input
*/
public function update(User $user, array $input): void
{
Validator::make($input, [
'name' => ['required', 'string', 'max:255'],
'email' => [
'required',
'string',
'email',
'max:255',
Rule::unique('users')->ignore($user->id),
],
])->validateWithBag('updateProfileInformation');
if ($input['email'] !== $user->email &&
$user instanceof MustVerifyEmail) {
$this->updateVerifiedUser($user, $input);
} else {
$user->forceFill([
'name' => $input['name'],
'email' => $input['email'],
])->save();
}
}
/**
* Update the given verified user's profile information.
*
* @param array<string, string> $input
*/
protected function updateVerifiedUser(User $user, array $input): void
{
$user->forceFill([
'name' => $input['name'],
'email' => $input['email'],
'email_verified_at' => null,
])->save();
$user->sendEmailVerificationNotification();
}
}

View File

@@ -0,0 +1,26 @@
<?php
namespace App\Http\Livewire;
use App\Models\Team;
use Livewire\Component;
class SwitchTeam extends Component
{
public function switch_to($team_id)
{
if (!auth()->user()->teams->contains($team_id)) {
return;
}
$team_to_switch_to = Team::find($team_id);
if (!$team_to_switch_to) {
return;
}
session(['currentTeam' => $team_to_switch_to]);
return redirect(request()->header('Referer'));
}
public function render()
{
return view('livewire.switch-team');
}
}

18
app/Models/BaseModel.php Normal file
View File

@@ -0,0 +1,18 @@
<?php
namespace App\Models;
use Illuminate\Database\Eloquent\Model;
use Xaevik\Cuid2\Cuid2;
abstract class BaseModel extends Model
{
protected static function boot()
{
parent::boot();
static::creating(function (Model $model) {
$model->uuid = (string) new Cuid2();
});
}
}

13
app/Models/Team.php Normal file
View File

@@ -0,0 +1,13 @@
<?php
namespace App\Models;
class Team extends BaseModel
{
protected $casts = [
'personal_team' => 'boolean',
];
protected $fillable = [
'name',
];
}

View File

@@ -2,11 +2,12 @@
namespace App\Models;
// use Illuminate\Contracts\Auth\MustVerifyEmail;
use Illuminate\Database\Eloquent\Factories\HasFactory;
use Illuminate\Database\Eloquent\Model;
use Illuminate\Foundation\Auth\User as Authenticatable;
use Illuminate\Notifications\Notifiable;
use Laravel\Sanctum\HasApiTokens;
use Xaevik\Cuid2\Cuid2;
class User extends Authenticatable
{
@@ -41,4 +42,28 @@ class User extends Authenticatable
protected $casts = [
'email_verified_at' => 'datetime',
];
protected static function boot()
{
parent::boot();
static::creating(function (Model $model) {
$model->uuid = (string) new Cuid2();
});
}
public function teams()
{
return $this->belongsToMany(Team::class);
}
public function currentTeam()
{
return $this->belongsTo(Team::class);
}
public function otherTeams()
{
$team_id = session('currentTeam')->id;
return auth()->user()->teams->filter(function ($team) use ($team_id) {
return $team->id != $team_id;
});
}
}

View File

@@ -0,0 +1,60 @@
<?php
namespace App\Providers;
use App\Actions\Fortify\CreateNewUser;
use App\Actions\Fortify\ResetUserPassword;
use App\Actions\Fortify\UpdateUserPassword;
use App\Actions\Fortify\UpdateUserProfileInformation;
use App\Models\User;
use Illuminate\Cache\RateLimiting\Limit;
use Illuminate\Http\Request;
use Illuminate\Support\Facades\Hash;
use Illuminate\Support\Facades\RateLimiter;
use Illuminate\Support\ServiceProvider;
use Laravel\Fortify\Fortify;
class FortifyServiceProvider extends ServiceProvider
{
/**
* Register any application services.
*/
public function register(): void
{
//
}
/**
* Bootstrap any application services.
*/
public function boot(): void
{
Fortify::loginView(fn () => view('auth.login'));
Fortify::registerView(fn () => view('auth.register'));
Fortify::authenticateUsing(function (Request $request) {
$user = User::where('email', $request->email)->with('teams')->first();
if (
$user &&
Hash::check($request->password, $user->password)
) {
session(['currentTeam' => $user->currentTeam = $user->teams->firstWhere('personal_team', true)]);
return $user;
}
});
Fortify::createUsersUsing(CreateNewUser::class);
Fortify::updateUserProfileInformationUsing(UpdateUserProfileInformation::class);
Fortify::updateUserPasswordsUsing(UpdateUserPassword::class);
Fortify::resetUserPasswordsUsing(ResetUserPassword::class);
RateLimiter::for('login', function (Request $request) {
$email = (string) $request->email;
return Limit::perMinute(5)->by($email.$request->ip());
});
RateLimiter::for('two-factor', function (Request $request) {
return Limit::perMinute(5)->by($request->session()->get('login.id'));
});
}
}

View File

@@ -17,7 +17,7 @@ class RouteServiceProvider extends ServiceProvider
*
* @var string
*/
public const HOME = '/home';
public const HOME = '/';
/**
* Define your route model bindings, pattern filters, and other route configuration.