Add users, teams, authentication, profile/login/register/navbar views
This commit is contained in:
parent
436d22e385
commit
e47d493776
40
app/Actions/Fortify/CreateNewUser.php
Normal file
40
app/Actions/Fortify/CreateNewUser.php
Normal 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']),
|
||||||
|
]);
|
||||||
|
}
|
||||||
|
}
|
18
app/Actions/Fortify/PasswordValidationRules.php
Normal file
18
app/Actions/Fortify/PasswordValidationRules.php
Normal 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'];
|
||||||
|
}
|
||||||
|
}
|
29
app/Actions/Fortify/ResetUserPassword.php
Normal file
29
app/Actions/Fortify/ResetUserPassword.php
Normal 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();
|
||||||
|
}
|
||||||
|
}
|
32
app/Actions/Fortify/UpdateUserPassword.php
Normal file
32
app/Actions/Fortify/UpdateUserPassword.php
Normal 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();
|
||||||
|
}
|
||||||
|
}
|
58
app/Actions/Fortify/UpdateUserProfileInformation.php
Normal file
58
app/Actions/Fortify/UpdateUserProfileInformation.php
Normal 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();
|
||||||
|
}
|
||||||
|
}
|
26
app/Http/Livewire/SwitchTeam.php
Normal file
26
app/Http/Livewire/SwitchTeam.php
Normal 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
18
app/Models/BaseModel.php
Normal 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
13
app/Models/Team.php
Normal file
@ -0,0 +1,13 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace App\Models;
|
||||||
|
|
||||||
|
class Team extends BaseModel
|
||||||
|
{
|
||||||
|
protected $casts = [
|
||||||
|
'personal_team' => 'boolean',
|
||||||
|
];
|
||||||
|
protected $fillable = [
|
||||||
|
'name',
|
||||||
|
];
|
||||||
|
}
|
@ -2,11 +2,12 @@
|
|||||||
|
|
||||||
namespace App\Models;
|
namespace App\Models;
|
||||||
|
|
||||||
// use Illuminate\Contracts\Auth\MustVerifyEmail;
|
|
||||||
use Illuminate\Database\Eloquent\Factories\HasFactory;
|
use Illuminate\Database\Eloquent\Factories\HasFactory;
|
||||||
|
use Illuminate\Database\Eloquent\Model;
|
||||||
use Illuminate\Foundation\Auth\User as Authenticatable;
|
use Illuminate\Foundation\Auth\User as Authenticatable;
|
||||||
use Illuminate\Notifications\Notifiable;
|
use Illuminate\Notifications\Notifiable;
|
||||||
use Laravel\Sanctum\HasApiTokens;
|
use Laravel\Sanctum\HasApiTokens;
|
||||||
|
use Xaevik\Cuid2\Cuid2;
|
||||||
|
|
||||||
class User extends Authenticatable
|
class User extends Authenticatable
|
||||||
{
|
{
|
||||||
@ -41,4 +42,28 @@ class User extends Authenticatable
|
|||||||
protected $casts = [
|
protected $casts = [
|
||||||
'email_verified_at' => 'datetime',
|
'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;
|
||||||
|
});
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
60
app/Providers/FortifyServiceProvider.php
Normal file
60
app/Providers/FortifyServiceProvider.php
Normal 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'));
|
||||||
|
});
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
@ -17,7 +17,7 @@ class RouteServiceProvider extends ServiceProvider
|
|||||||
*
|
*
|
||||||
* @var string
|
* @var string
|
||||||
*/
|
*/
|
||||||
public const HOME = '/home';
|
public const HOME = '/';
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Define your route model bindings, pattern filters, and other route configuration.
|
* Define your route model bindings, pattern filters, and other route configuration.
|
||||||
|
@ -7,13 +7,15 @@
|
|||||||
"require": {
|
"require": {
|
||||||
"php": "^8.2",
|
"php": "^8.2",
|
||||||
"guzzlehttp/guzzle": "^7.2",
|
"guzzlehttp/guzzle": "^7.2",
|
||||||
|
"laravel/fortify": "^1.16",
|
||||||
"laravel/framework": "^10.0",
|
"laravel/framework": "^10.0",
|
||||||
"laravel/sanctum": "^3.2",
|
"laravel/sanctum": "^3.2",
|
||||||
"laravel/tinker": "^2.8",
|
"laravel/tinker": "^2.8",
|
||||||
"livewire/livewire": "^2.12",
|
"livewire/livewire": "^2.12",
|
||||||
"spatie/laravel-activitylog": "^4.7",
|
"spatie/laravel-activitylog": "^4.7",
|
||||||
"spatie/laravel-data": "^3.2",
|
"spatie/laravel-data": "^3.2",
|
||||||
"spatie/laravel-ray": "^1.32"
|
"spatie/laravel-ray": "^1.32",
|
||||||
|
"xaevik/cuid2": "^1.7"
|
||||||
},
|
},
|
||||||
"require-dev": {
|
"require-dev": {
|
||||||
"fakerphp/faker": "^1.9.1",
|
"fakerphp/faker": "^1.9.1",
|
||||||
|
344
composer.lock
generated
344
composer.lock
generated
@ -4,8 +4,62 @@
|
|||||||
"Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies",
|
"Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies",
|
||||||
"This file is @generated automatically"
|
"This file is @generated automatically"
|
||||||
],
|
],
|
||||||
"content-hash": "c6bfcd94dc256f9124e84a380eaaf556",
|
"content-hash": "0860841db6def79a62c268b0858d1f8a",
|
||||||
"packages": [
|
"packages": [
|
||||||
|
{
|
||||||
|
"name": "bacon/bacon-qr-code",
|
||||||
|
"version": "2.0.8",
|
||||||
|
"source": {
|
||||||
|
"type": "git",
|
||||||
|
"url": "https://github.com/Bacon/BaconQrCode.git",
|
||||||
|
"reference": "8674e51bb65af933a5ffaf1c308a660387c35c22"
|
||||||
|
},
|
||||||
|
"dist": {
|
||||||
|
"type": "zip",
|
||||||
|
"url": "https://api.github.com/repos/Bacon/BaconQrCode/zipball/8674e51bb65af933a5ffaf1c308a660387c35c22",
|
||||||
|
"reference": "8674e51bb65af933a5ffaf1c308a660387c35c22",
|
||||||
|
"shasum": ""
|
||||||
|
},
|
||||||
|
"require": {
|
||||||
|
"dasprid/enum": "^1.0.3",
|
||||||
|
"ext-iconv": "*",
|
||||||
|
"php": "^7.1 || ^8.0"
|
||||||
|
},
|
||||||
|
"require-dev": {
|
||||||
|
"phly/keep-a-changelog": "^2.1",
|
||||||
|
"phpunit/phpunit": "^7 | ^8 | ^9",
|
||||||
|
"spatie/phpunit-snapshot-assertions": "^4.2.9",
|
||||||
|
"squizlabs/php_codesniffer": "^3.4"
|
||||||
|
},
|
||||||
|
"suggest": {
|
||||||
|
"ext-imagick": "to generate QR code images"
|
||||||
|
},
|
||||||
|
"type": "library",
|
||||||
|
"autoload": {
|
||||||
|
"psr-4": {
|
||||||
|
"BaconQrCode\\": "src/"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"notification-url": "https://packagist.org/downloads/",
|
||||||
|
"license": [
|
||||||
|
"BSD-2-Clause"
|
||||||
|
],
|
||||||
|
"authors": [
|
||||||
|
{
|
||||||
|
"name": "Ben Scholzen 'DASPRiD'",
|
||||||
|
"email": "mail@dasprids.de",
|
||||||
|
"homepage": "https://dasprids.de/",
|
||||||
|
"role": "Developer"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"description": "BaconQrCode is a QR code generator for PHP.",
|
||||||
|
"homepage": "https://github.com/Bacon/BaconQrCode",
|
||||||
|
"support": {
|
||||||
|
"issues": "https://github.com/Bacon/BaconQrCode/issues",
|
||||||
|
"source": "https://github.com/Bacon/BaconQrCode/tree/2.0.8"
|
||||||
|
},
|
||||||
|
"time": "2022-12-07T17:46:57+00:00"
|
||||||
|
},
|
||||||
{
|
{
|
||||||
"name": "brick/math",
|
"name": "brick/math",
|
||||||
"version": "0.10.2",
|
"version": "0.10.2",
|
||||||
@ -62,6 +116,56 @@
|
|||||||
],
|
],
|
||||||
"time": "2022-08-10T22:54:19+00:00"
|
"time": "2022-08-10T22:54:19+00:00"
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
"name": "dasprid/enum",
|
||||||
|
"version": "1.0.4",
|
||||||
|
"source": {
|
||||||
|
"type": "git",
|
||||||
|
"url": "https://github.com/DASPRiD/Enum.git",
|
||||||
|
"reference": "8e6b6ea76eabbf19ea2bf5b67b98e1860474012f"
|
||||||
|
},
|
||||||
|
"dist": {
|
||||||
|
"type": "zip",
|
||||||
|
"url": "https://api.github.com/repos/DASPRiD/Enum/zipball/8e6b6ea76eabbf19ea2bf5b67b98e1860474012f",
|
||||||
|
"reference": "8e6b6ea76eabbf19ea2bf5b67b98e1860474012f",
|
||||||
|
"shasum": ""
|
||||||
|
},
|
||||||
|
"require": {
|
||||||
|
"php": ">=7.1 <9.0"
|
||||||
|
},
|
||||||
|
"require-dev": {
|
||||||
|
"phpunit/phpunit": "^7 | ^8 | ^9",
|
||||||
|
"squizlabs/php_codesniffer": "*"
|
||||||
|
},
|
||||||
|
"type": "library",
|
||||||
|
"autoload": {
|
||||||
|
"psr-4": {
|
||||||
|
"DASPRiD\\Enum\\": "src/"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"notification-url": "https://packagist.org/downloads/",
|
||||||
|
"license": [
|
||||||
|
"BSD-2-Clause"
|
||||||
|
],
|
||||||
|
"authors": [
|
||||||
|
{
|
||||||
|
"name": "Ben Scholzen 'DASPRiD'",
|
||||||
|
"email": "mail@dasprids.de",
|
||||||
|
"homepage": "https://dasprids.de/",
|
||||||
|
"role": "Developer"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"description": "PHP 7.1 enum implementation",
|
||||||
|
"keywords": [
|
||||||
|
"enum",
|
||||||
|
"map"
|
||||||
|
],
|
||||||
|
"support": {
|
||||||
|
"issues": "https://github.com/DASPRiD/Enum/issues",
|
||||||
|
"source": "https://github.com/DASPRiD/Enum/tree/1.0.4"
|
||||||
|
},
|
||||||
|
"time": "2023-03-01T18:44:03+00:00"
|
||||||
|
},
|
||||||
{
|
{
|
||||||
"name": "dflydev/dot-access-data",
|
"name": "dflydev/dot-access-data",
|
||||||
"version": "v3.0.2",
|
"version": "v3.0.2",
|
||||||
@ -1024,6 +1128,69 @@
|
|||||||
],
|
],
|
||||||
"time": "2021-10-07T12:57:01+00:00"
|
"time": "2021-10-07T12:57:01+00:00"
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
"name": "laravel/fortify",
|
||||||
|
"version": "v1.16.0",
|
||||||
|
"source": {
|
||||||
|
"type": "git",
|
||||||
|
"url": "https://github.com/laravel/fortify.git",
|
||||||
|
"reference": "e626fc70fcd940d01326c6c44512398cccc3113c"
|
||||||
|
},
|
||||||
|
"dist": {
|
||||||
|
"type": "zip",
|
||||||
|
"url": "https://api.github.com/repos/laravel/fortify/zipball/e626fc70fcd940d01326c6c44512398cccc3113c",
|
||||||
|
"reference": "e626fc70fcd940d01326c6c44512398cccc3113c",
|
||||||
|
"shasum": ""
|
||||||
|
},
|
||||||
|
"require": {
|
||||||
|
"bacon/bacon-qr-code": "^2.0",
|
||||||
|
"ext-json": "*",
|
||||||
|
"illuminate/support": "^8.82|^9.0|^10.0",
|
||||||
|
"php": "^7.3|^8.0",
|
||||||
|
"pragmarx/google2fa": "^7.0|^8.0"
|
||||||
|
},
|
||||||
|
"require-dev": {
|
||||||
|
"mockery/mockery": "^1.0",
|
||||||
|
"orchestra/testbench": "^6.0|^7.0|^8.0",
|
||||||
|
"phpunit/phpunit": "^9.3"
|
||||||
|
},
|
||||||
|
"type": "library",
|
||||||
|
"extra": {
|
||||||
|
"branch-alias": {
|
||||||
|
"dev-master": "1.x-dev"
|
||||||
|
},
|
||||||
|
"laravel": {
|
||||||
|
"providers": [
|
||||||
|
"Laravel\\Fortify\\FortifyServiceProvider"
|
||||||
|
]
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"autoload": {
|
||||||
|
"psr-4": {
|
||||||
|
"Laravel\\Fortify\\": "src/"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"notification-url": "https://packagist.org/downloads/",
|
||||||
|
"license": [
|
||||||
|
"MIT"
|
||||||
|
],
|
||||||
|
"authors": [
|
||||||
|
{
|
||||||
|
"name": "Taylor Otwell",
|
||||||
|
"email": "taylor@laravel.com"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"description": "Backend controllers and scaffolding for Laravel authentication.",
|
||||||
|
"keywords": [
|
||||||
|
"auth",
|
||||||
|
"laravel"
|
||||||
|
],
|
||||||
|
"support": {
|
||||||
|
"issues": "https://github.com/laravel/fortify/issues",
|
||||||
|
"source": "https://github.com/laravel/fortify"
|
||||||
|
},
|
||||||
|
"time": "2023-01-06T15:57:08+00:00"
|
||||||
|
},
|
||||||
{
|
{
|
||||||
"name": "laravel/framework",
|
"name": "laravel/framework",
|
||||||
"version": "v10.3.3",
|
"version": "v10.3.3",
|
||||||
@ -2319,6 +2486,73 @@
|
|||||||
],
|
],
|
||||||
"time": "2023-02-08T01:06:31+00:00"
|
"time": "2023-02-08T01:06:31+00:00"
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
"name": "paragonie/constant_time_encoding",
|
||||||
|
"version": "v2.6.3",
|
||||||
|
"source": {
|
||||||
|
"type": "git",
|
||||||
|
"url": "https://github.com/paragonie/constant_time_encoding.git",
|
||||||
|
"reference": "58c3f47f650c94ec05a151692652a868995d2938"
|
||||||
|
},
|
||||||
|
"dist": {
|
||||||
|
"type": "zip",
|
||||||
|
"url": "https://api.github.com/repos/paragonie/constant_time_encoding/zipball/58c3f47f650c94ec05a151692652a868995d2938",
|
||||||
|
"reference": "58c3f47f650c94ec05a151692652a868995d2938",
|
||||||
|
"shasum": ""
|
||||||
|
},
|
||||||
|
"require": {
|
||||||
|
"php": "^7|^8"
|
||||||
|
},
|
||||||
|
"require-dev": {
|
||||||
|
"phpunit/phpunit": "^6|^7|^8|^9",
|
||||||
|
"vimeo/psalm": "^1|^2|^3|^4"
|
||||||
|
},
|
||||||
|
"type": "library",
|
||||||
|
"autoload": {
|
||||||
|
"psr-4": {
|
||||||
|
"ParagonIE\\ConstantTime\\": "src/"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"notification-url": "https://packagist.org/downloads/",
|
||||||
|
"license": [
|
||||||
|
"MIT"
|
||||||
|
],
|
||||||
|
"authors": [
|
||||||
|
{
|
||||||
|
"name": "Paragon Initiative Enterprises",
|
||||||
|
"email": "security@paragonie.com",
|
||||||
|
"homepage": "https://paragonie.com",
|
||||||
|
"role": "Maintainer"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "Steve 'Sc00bz' Thomas",
|
||||||
|
"email": "steve@tobtu.com",
|
||||||
|
"homepage": "https://www.tobtu.com",
|
||||||
|
"role": "Original Developer"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"description": "Constant-time Implementations of RFC 4648 Encoding (Base-64, Base-32, Base-16)",
|
||||||
|
"keywords": [
|
||||||
|
"base16",
|
||||||
|
"base32",
|
||||||
|
"base32_decode",
|
||||||
|
"base32_encode",
|
||||||
|
"base64",
|
||||||
|
"base64_decode",
|
||||||
|
"base64_encode",
|
||||||
|
"bin2hex",
|
||||||
|
"encoding",
|
||||||
|
"hex",
|
||||||
|
"hex2bin",
|
||||||
|
"rfc4648"
|
||||||
|
],
|
||||||
|
"support": {
|
||||||
|
"email": "info@paragonie.com",
|
||||||
|
"issues": "https://github.com/paragonie/constant_time_encoding/issues",
|
||||||
|
"source": "https://github.com/paragonie/constant_time_encoding"
|
||||||
|
},
|
||||||
|
"time": "2022-06-14T06:56:20+00:00"
|
||||||
|
},
|
||||||
{
|
{
|
||||||
"name": "phpdocumentor/reflection-common",
|
"name": "phpdocumentor/reflection-common",
|
||||||
"version": "2.2.0",
|
"version": "2.2.0",
|
||||||
@ -2603,6 +2837,58 @@
|
|||||||
},
|
},
|
||||||
"time": "2021-10-28T11:13:42+00:00"
|
"time": "2021-10-28T11:13:42+00:00"
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
"name": "pragmarx/google2fa",
|
||||||
|
"version": "v8.0.1",
|
||||||
|
"source": {
|
||||||
|
"type": "git",
|
||||||
|
"url": "https://github.com/antonioribeiro/google2fa.git",
|
||||||
|
"reference": "80c3d801b31fe165f8fe99ea085e0a37834e1be3"
|
||||||
|
},
|
||||||
|
"dist": {
|
||||||
|
"type": "zip",
|
||||||
|
"url": "https://api.github.com/repos/antonioribeiro/google2fa/zipball/80c3d801b31fe165f8fe99ea085e0a37834e1be3",
|
||||||
|
"reference": "80c3d801b31fe165f8fe99ea085e0a37834e1be3",
|
||||||
|
"shasum": ""
|
||||||
|
},
|
||||||
|
"require": {
|
||||||
|
"paragonie/constant_time_encoding": "^1.0|^2.0",
|
||||||
|
"php": "^7.1|^8.0"
|
||||||
|
},
|
||||||
|
"require-dev": {
|
||||||
|
"phpstan/phpstan": "^0.12.18",
|
||||||
|
"phpunit/phpunit": "^7.5.15|^8.5|^9.0"
|
||||||
|
},
|
||||||
|
"type": "library",
|
||||||
|
"autoload": {
|
||||||
|
"psr-4": {
|
||||||
|
"PragmaRX\\Google2FA\\": "src/"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"notification-url": "https://packagist.org/downloads/",
|
||||||
|
"license": [
|
||||||
|
"MIT"
|
||||||
|
],
|
||||||
|
"authors": [
|
||||||
|
{
|
||||||
|
"name": "Antonio Carlos Ribeiro",
|
||||||
|
"email": "acr@antoniocarlosribeiro.com",
|
||||||
|
"role": "Creator & Designer"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"description": "A One Time Password Authentication package, compatible with Google Authenticator.",
|
||||||
|
"keywords": [
|
||||||
|
"2fa",
|
||||||
|
"Authentication",
|
||||||
|
"Two Factor Authentication",
|
||||||
|
"google2fa"
|
||||||
|
],
|
||||||
|
"support": {
|
||||||
|
"issues": "https://github.com/antonioribeiro/google2fa/issues",
|
||||||
|
"source": "https://github.com/antonioribeiro/google2fa/tree/v8.0.1"
|
||||||
|
},
|
||||||
|
"time": "2022-06-13T21:57:56+00:00"
|
||||||
|
},
|
||||||
{
|
{
|
||||||
"name": "psr/container",
|
"name": "psr/container",
|
||||||
"version": "2.0.2",
|
"version": "2.0.2",
|
||||||
@ -6384,6 +6670,62 @@
|
|||||||
},
|
},
|
||||||
"time": "2022-06-03T18:03:27+00:00"
|
"time": "2022-06-03T18:03:27+00:00"
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
"name": "xaevik/cuid2",
|
||||||
|
"version": "1.7.0",
|
||||||
|
"source": {
|
||||||
|
"type": "git",
|
||||||
|
"url": "git@gitlab.com:xaevik/php-cuid2.git",
|
||||||
|
"reference": "e435e5005b2e9e9e9d30b64025749c0b7aa5dcf7"
|
||||||
|
},
|
||||||
|
"dist": {
|
||||||
|
"type": "zip",
|
||||||
|
"url": "https://gitlab.com/api/v4/projects/xaevik%2Fphp-cuid2/repository/archive.zip?sha=e435e5005b2e9e9e9d30b64025749c0b7aa5dcf7",
|
||||||
|
"reference": "e435e5005b2e9e9e9d30b64025749c0b7aa5dcf7",
|
||||||
|
"shasum": ""
|
||||||
|
},
|
||||||
|
"require": {
|
||||||
|
"php": "^7.4 || ^8.0"
|
||||||
|
},
|
||||||
|
"require-dev": {
|
||||||
|
"ergebnis/composer-normalize": "^2.29",
|
||||||
|
"ext-ctype": "*",
|
||||||
|
"php-parallel-lint/php-parallel-lint": "^1.3",
|
||||||
|
"phpstan/phpstan": "^1.9",
|
||||||
|
"phpunit/phpunit": "^10.0",
|
||||||
|
"squizlabs/php_codesniffer": "^3.7",
|
||||||
|
"vimeo/psalm": "^5.4"
|
||||||
|
},
|
||||||
|
"suggest": {
|
||||||
|
"ext-gmp": "*"
|
||||||
|
},
|
||||||
|
"type": "library",
|
||||||
|
"autoload": {
|
||||||
|
"files": [
|
||||||
|
"src/compat.php"
|
||||||
|
],
|
||||||
|
"psr-4": {
|
||||||
|
"Xaevik\\Cuid2\\": "src/"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"notification-url": "https://packagist.org/downloads/",
|
||||||
|
"license": [
|
||||||
|
"Apache-2.0"
|
||||||
|
],
|
||||||
|
"authors": [
|
||||||
|
{
|
||||||
|
"name": "Alan Brault",
|
||||||
|
"email": "alan.brault@visus.io"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"description": "A PHP library for generating collision-resistant ids (CUIDs).",
|
||||||
|
"keywords": [
|
||||||
|
"cuid",
|
||||||
|
"identifier"
|
||||||
|
],
|
||||||
|
"abandoned": "visus/cuid2",
|
||||||
|
"time": "2023-02-21T14:01:02+00:00"
|
||||||
|
},
|
||||||
{
|
{
|
||||||
"name": "zbateson/mail-mime-parser",
|
"name": "zbateson/mail-mime-parser",
|
||||||
"version": "2.4.0",
|
"version": "2.4.0",
|
||||||
|
@ -190,6 +190,7 @@ return [
|
|||||||
* Application Service Providers...
|
* Application Service Providers...
|
||||||
*/
|
*/
|
||||||
App\Providers\AppServiceProvider::class,
|
App\Providers\AppServiceProvider::class,
|
||||||
|
App\Providers\FortifyServiceProvider::class,
|
||||||
App\Providers\AuthServiceProvider::class,
|
App\Providers\AuthServiceProvider::class,
|
||||||
// App\Providers\BroadcastServiceProvider::class,
|
// App\Providers\BroadcastServiceProvider::class,
|
||||||
App\Providers\EventServiceProvider::class,
|
App\Providers\EventServiceProvider::class,
|
||||||
|
147
config/fortify.php
Normal file
147
config/fortify.php
Normal file
@ -0,0 +1,147 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
use App\Providers\RouteServiceProvider;
|
||||||
|
use Laravel\Fortify\Features;
|
||||||
|
|
||||||
|
return [
|
||||||
|
|
||||||
|
/*
|
||||||
|
|--------------------------------------------------------------------------
|
||||||
|
| Fortify Guard
|
||||||
|
|--------------------------------------------------------------------------
|
||||||
|
|
|
||||||
|
| Here you may specify which authentication guard Fortify will use while
|
||||||
|
| authenticating users. This value should correspond with one of your
|
||||||
|
| guards that is already present in your "auth" configuration file.
|
||||||
|
|
|
||||||
|
*/
|
||||||
|
|
||||||
|
'guard' => 'web',
|
||||||
|
|
||||||
|
/*
|
||||||
|
|--------------------------------------------------------------------------
|
||||||
|
| Fortify Password Broker
|
||||||
|
|--------------------------------------------------------------------------
|
||||||
|
|
|
||||||
|
| Here you may specify which password broker Fortify can use when a user
|
||||||
|
| is resetting their password. This configured value should match one
|
||||||
|
| of your password brokers setup in your "auth" configuration file.
|
||||||
|
|
|
||||||
|
*/
|
||||||
|
|
||||||
|
'passwords' => 'users',
|
||||||
|
|
||||||
|
/*
|
||||||
|
|--------------------------------------------------------------------------
|
||||||
|
| Username / Email
|
||||||
|
|--------------------------------------------------------------------------
|
||||||
|
|
|
||||||
|
| This value defines which model attribute should be considered as your
|
||||||
|
| application's "username" field. Typically, this might be the email
|
||||||
|
| address of the users but you are free to change this value here.
|
||||||
|
|
|
||||||
|
| Out of the box, Fortify expects forgot password and reset password
|
||||||
|
| requests to have a field named 'email'. If the application uses
|
||||||
|
| another name for the field you may define it below as needed.
|
||||||
|
|
|
||||||
|
*/
|
||||||
|
|
||||||
|
'username' => 'email',
|
||||||
|
|
||||||
|
'email' => 'email',
|
||||||
|
|
||||||
|
/*
|
||||||
|
|--------------------------------------------------------------------------
|
||||||
|
| Home Path
|
||||||
|
|--------------------------------------------------------------------------
|
||||||
|
|
|
||||||
|
| Here you may configure the path where users will get redirected during
|
||||||
|
| authentication or password reset when the operations are successful
|
||||||
|
| and the user is authenticated. You are free to change this value.
|
||||||
|
|
|
||||||
|
*/
|
||||||
|
|
||||||
|
'home' => RouteServiceProvider::HOME,
|
||||||
|
|
||||||
|
/*
|
||||||
|
|--------------------------------------------------------------------------
|
||||||
|
| Fortify Routes Prefix / Subdomain
|
||||||
|
|--------------------------------------------------------------------------
|
||||||
|
|
|
||||||
|
| Here you may specify which prefix Fortify will assign to all the routes
|
||||||
|
| that it registers with the application. If necessary, you may change
|
||||||
|
| subdomain under which all of the Fortify routes will be available.
|
||||||
|
|
|
||||||
|
*/
|
||||||
|
|
||||||
|
'prefix' => '',
|
||||||
|
|
||||||
|
'domain' => null,
|
||||||
|
|
||||||
|
/*
|
||||||
|
|--------------------------------------------------------------------------
|
||||||
|
| Fortify Routes Middleware
|
||||||
|
|--------------------------------------------------------------------------
|
||||||
|
|
|
||||||
|
| Here you may specify which middleware Fortify will assign to the routes
|
||||||
|
| that it registers with the application. If necessary, you may change
|
||||||
|
| these middleware but typically this provided default is preferred.
|
||||||
|
|
|
||||||
|
*/
|
||||||
|
|
||||||
|
'middleware' => ['web'],
|
||||||
|
|
||||||
|
/*
|
||||||
|
|--------------------------------------------------------------------------
|
||||||
|
| Rate Limiting
|
||||||
|
|--------------------------------------------------------------------------
|
||||||
|
|
|
||||||
|
| By default, Fortify will throttle logins to five requests per minute for
|
||||||
|
| every email and IP address combination. However, if you would like to
|
||||||
|
| specify a custom rate limiter to call then you may specify it here.
|
||||||
|
|
|
||||||
|
*/
|
||||||
|
|
||||||
|
'limiters' => [
|
||||||
|
'login' => 'login',
|
||||||
|
'two-factor' => 'two-factor',
|
||||||
|
],
|
||||||
|
|
||||||
|
/*
|
||||||
|
|--------------------------------------------------------------------------
|
||||||
|
| Register View Routes
|
||||||
|
|--------------------------------------------------------------------------
|
||||||
|
|
|
||||||
|
| Here you may specify if the routes returning views should be disabled as
|
||||||
|
| you may not need them when building your own application. This may be
|
||||||
|
| especially true if you're writing a custom single-page application.
|
||||||
|
|
|
||||||
|
*/
|
||||||
|
|
||||||
|
'views' => true,
|
||||||
|
|
||||||
|
/*
|
||||||
|
|--------------------------------------------------------------------------
|
||||||
|
| Features
|
||||||
|
|--------------------------------------------------------------------------
|
||||||
|
|
|
||||||
|
| Some of the Fortify features are optional. You may disable the features
|
||||||
|
| by removing them from this array. You're free to only remove some of
|
||||||
|
| these features or you can even remove all of these if you need to.
|
||||||
|
|
|
||||||
|
*/
|
||||||
|
|
||||||
|
'features' => [
|
||||||
|
Features::registration(),
|
||||||
|
Features::resetPasswords(),
|
||||||
|
// Features::emailVerification(),
|
||||||
|
Features::updateProfileInformation(),
|
||||||
|
Features::updatePasswords(),
|
||||||
|
Features::twoFactorAuthentication([
|
||||||
|
'confirm' => true,
|
||||||
|
'confirmPassword' => true,
|
||||||
|
// 'window' => 0,
|
||||||
|
]),
|
||||||
|
],
|
||||||
|
|
||||||
|
];
|
@ -13,6 +13,8 @@ return new class extends Migration
|
|||||||
{
|
{
|
||||||
Schema::create('users', function (Blueprint $table) {
|
Schema::create('users', function (Blueprint $table) {
|
||||||
$table->id();
|
$table->id();
|
||||||
|
$table->string('uuid')->unique();
|
||||||
|
$table->boolean('is_root_user')->default(false);
|
||||||
$table->string('name');
|
$table->string('name');
|
||||||
$table->string('email')->unique();
|
$table->string('email')->unique();
|
||||||
$table->timestamp('email_verified_at')->nullable();
|
$table->timestamp('email_verified_at')->nullable();
|
||||||
|
@ -0,0 +1,46 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
use Illuminate\Database\Migrations\Migration;
|
||||||
|
use Illuminate\Database\Schema\Blueprint;
|
||||||
|
use Illuminate\Support\Facades\Schema;
|
||||||
|
use Laravel\Fortify\Fortify;
|
||||||
|
|
||||||
|
return new class extends Migration
|
||||||
|
{
|
||||||
|
/**
|
||||||
|
* Run the migrations.
|
||||||
|
*/
|
||||||
|
public function up(): void
|
||||||
|
{
|
||||||
|
Schema::table('users', function (Blueprint $table) {
|
||||||
|
$table->text('two_factor_secret')
|
||||||
|
->after('password')
|
||||||
|
->nullable();
|
||||||
|
|
||||||
|
$table->text('two_factor_recovery_codes')
|
||||||
|
->after('two_factor_secret')
|
||||||
|
->nullable();
|
||||||
|
|
||||||
|
if (Fortify::confirmsTwoFactorAuthentication()) {
|
||||||
|
$table->timestamp('two_factor_confirmed_at')
|
||||||
|
->after('two_factor_recovery_codes')
|
||||||
|
->nullable();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Reverse the migrations.
|
||||||
|
*/
|
||||||
|
public function down(): void
|
||||||
|
{
|
||||||
|
Schema::table('users', function (Blueprint $table) {
|
||||||
|
$table->dropColumn(array_merge([
|
||||||
|
'two_factor_secret',
|
||||||
|
'two_factor_recovery_codes',
|
||||||
|
], Fortify::confirmsTwoFactorAuthentication() ? [
|
||||||
|
'two_factor_confirmed_at',
|
||||||
|
] : []));
|
||||||
|
});
|
||||||
|
}
|
||||||
|
};
|
@ -16,6 +16,7 @@ return new class extends Migration
|
|||||||
$table->morphs('tokenable');
|
$table->morphs('tokenable');
|
||||||
$table->string('name');
|
$table->string('name');
|
||||||
$table->string('token', 64)->unique();
|
$table->string('token', 64)->unique();
|
||||||
|
$table->string('team_id');
|
||||||
$table->text('abilities')->nullable();
|
$table->text('abilities')->nullable();
|
||||||
$table->timestamp('last_used_at')->nullable();
|
$table->timestamp('last_used_at')->nullable();
|
||||||
$table->timestamp('expires_at')->nullable();
|
$table->timestamp('expires_at')->nullable();
|
||||||
|
30
database/migrations/2023_03_20_112811_create_teams_table.php
Normal file
30
database/migrations/2023_03_20_112811_create_teams_table.php
Normal file
@ -0,0 +1,30 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
use Illuminate\Database\Migrations\Migration;
|
||||||
|
use Illuminate\Database\Schema\Blueprint;
|
||||||
|
use Illuminate\Support\Facades\Schema;
|
||||||
|
|
||||||
|
return new class extends Migration
|
||||||
|
{
|
||||||
|
/**
|
||||||
|
* Run the migrations.
|
||||||
|
*/
|
||||||
|
public function up(): void
|
||||||
|
{
|
||||||
|
Schema::create('teams', function (Blueprint $table) {
|
||||||
|
$table->id();
|
||||||
|
$table->string('uuid')->unique();
|
||||||
|
$table->string('name');
|
||||||
|
$table->boolean('personal_team')->default(false);
|
||||||
|
$table->timestamps();
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Reverse the migrations.
|
||||||
|
*/
|
||||||
|
public function down(): void
|
||||||
|
{
|
||||||
|
Schema::dropIfExists('teams');
|
||||||
|
}
|
||||||
|
};
|
@ -0,0 +1,32 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
use Illuminate\Database\Migrations\Migration;
|
||||||
|
use Illuminate\Database\Schema\Blueprint;
|
||||||
|
use Illuminate\Support\Facades\Schema;
|
||||||
|
|
||||||
|
return new class extends Migration
|
||||||
|
{
|
||||||
|
/**
|
||||||
|
* Run the migrations.
|
||||||
|
*/
|
||||||
|
public function up(): void
|
||||||
|
{
|
||||||
|
Schema::create('team_user', function (Blueprint $table) {
|
||||||
|
$table->id();
|
||||||
|
$table->foreignId('team_id');
|
||||||
|
$table->foreignId('user_id');
|
||||||
|
$table->string('role')->nullable();
|
||||||
|
$table->timestamps();
|
||||||
|
|
||||||
|
$table->unique(['team_id', 'user_id']);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Reverse the migrations.
|
||||||
|
*/
|
||||||
|
public function down(): void
|
||||||
|
{
|
||||||
|
Schema::dropIfExists('team_user');
|
||||||
|
}
|
||||||
|
};
|
@ -2,21 +2,17 @@
|
|||||||
|
|
||||||
namespace Database\Seeders;
|
namespace Database\Seeders;
|
||||||
|
|
||||||
// use Illuminate\Database\Console\Seeds\WithoutModelEvents;
|
|
||||||
use Illuminate\Database\Seeder;
|
use Illuminate\Database\Seeder;
|
||||||
|
|
||||||
class DatabaseSeeder extends Seeder
|
class DatabaseSeeder extends Seeder
|
||||||
{
|
{
|
||||||
/**
|
|
||||||
* Seed the application's database.
|
|
||||||
*/
|
|
||||||
public function run(): void
|
public function run(): void
|
||||||
{
|
{
|
||||||
// \App\Models\User::factory(10)->create();
|
if (env('APP_ENV') === 'local') {
|
||||||
|
$this->call([
|
||||||
// \App\Models\User::factory()->create([
|
UserSeeder::class,
|
||||||
// 'name' => 'Test User',
|
TeamSeeder::class,
|
||||||
// 'email' => 'test@example.com',
|
]);
|
||||||
// ]);
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
45
database/seeders/TeamSeeder.php
Normal file
45
database/seeders/TeamSeeder.php
Normal file
@ -0,0 +1,45 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace Database\Seeders;
|
||||||
|
|
||||||
|
use App\Models\Team;
|
||||||
|
use App\Models\User;
|
||||||
|
use Illuminate\Database\Seeder;
|
||||||
|
use Illuminate\Support\Facades\DB;
|
||||||
|
|
||||||
|
class TeamSeeder extends Seeder
|
||||||
|
{
|
||||||
|
public function run(): void
|
||||||
|
{
|
||||||
|
$root_user = User::find(1);
|
||||||
|
$normal_user = User::find(2);
|
||||||
|
|
||||||
|
$root_user_personal_team = Team::create([
|
||||||
|
'name' => "Root Team",
|
||||||
|
'personal_team' => true,
|
||||||
|
]);
|
||||||
|
$root_user_other_team = Team::create([
|
||||||
|
'name' => "Root User's Other Team",
|
||||||
|
'personal_team' => false,
|
||||||
|
]);
|
||||||
|
$normal_user_personal_team = Team::create([
|
||||||
|
'name' => 'Normal Team',
|
||||||
|
'personal_team' => true,
|
||||||
|
]);
|
||||||
|
DB::table('team_user')->insert([
|
||||||
|
'team_id' => $root_user_personal_team->id,
|
||||||
|
'user_id' => $root_user->id,
|
||||||
|
'role' => 'admin',
|
||||||
|
]);
|
||||||
|
DB::table('team_user')->insert([
|
||||||
|
'team_id' => $root_user_other_team->id,
|
||||||
|
'user_id' => $root_user->id,
|
||||||
|
'role' => 'admin',
|
||||||
|
]);
|
||||||
|
DB::table('team_user')->insert([
|
||||||
|
'team_id' => $normal_user_personal_team->id,
|
||||||
|
'user_id' => $normal_user->id,
|
||||||
|
'role' => 'admin',
|
||||||
|
]);
|
||||||
|
}
|
||||||
|
}
|
26
database/seeders/UserSeeder.php
Normal file
26
database/seeders/UserSeeder.php
Normal file
@ -0,0 +1,26 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace Database\Seeders;
|
||||||
|
|
||||||
|
use App\Models\Team;
|
||||||
|
use App\Models\User;
|
||||||
|
use Illuminate\Database\Seeder;
|
||||||
|
use Illuminate\Support\Facades\DB;
|
||||||
|
|
||||||
|
class UserSeeder extends Seeder
|
||||||
|
{
|
||||||
|
public function run(): void
|
||||||
|
{
|
||||||
|
User::factory()->create([
|
||||||
|
'id' => 1,
|
||||||
|
'name' => 'Root User',
|
||||||
|
'email' => 'test@example.com',
|
||||||
|
'is_root_user' => true,
|
||||||
|
]);
|
||||||
|
User::factory()->create([
|
||||||
|
'id' => 2,
|
||||||
|
'name' => 'Normal User',
|
||||||
|
'email' => 'test2@example.com',
|
||||||
|
]);
|
||||||
|
}
|
||||||
|
}
|
20
resources/views/auth/login.blade.php
Normal file
20
resources/views/auth/login.blade.php
Normal file
@ -0,0 +1,20 @@
|
|||||||
|
<x-layout>
|
||||||
|
<div>
|
||||||
|
<form action="/login" method="POST">
|
||||||
|
@csrf
|
||||||
|
<input type="text" name="email" placeholder="email" @env('local') value="test@example.com" @endenv
|
||||||
|
autofocus />
|
||||||
|
<input type="password" name="password" placeho lder="Password" @env('local') value="password" @endenv />
|
||||||
|
<button type="submit">Login</button>
|
||||||
|
</form>
|
||||||
|
@if ($errors->any())
|
||||||
|
<div class="alert alert-danger">
|
||||||
|
<ul>
|
||||||
|
@foreach ($errors->all() as $error)
|
||||||
|
<li>{{ $error }}</li>
|
||||||
|
@endforeach
|
||||||
|
</ul>
|
||||||
|
</div>
|
||||||
|
@endif
|
||||||
|
</div>
|
||||||
|
</x-layout>
|
20
resources/views/auth/register.blade.php
Normal file
20
resources/views/auth/register.blade.php
Normal file
@ -0,0 +1,20 @@
|
|||||||
|
<x-layout>
|
||||||
|
<form action="/register" method="POST">
|
||||||
|
@csrf
|
||||||
|
<input type="text" name="name" placeholder="name" @env('local') value="Andras Bacsai" @endenv />
|
||||||
|
<input type="text" name="email" placeholder="email" @env('local') value="andras@bacsai.com" @endenv />
|
||||||
|
<input type="password" name="password" placeholder="Password" @env('local') value="password" @endenv />
|
||||||
|
<input type="password" name="password_confirmation" placeholder="Password"
|
||||||
|
@env('local') value="password" @endenv />
|
||||||
|
<button type="submit">Register</button>
|
||||||
|
</form>
|
||||||
|
@if ($errors->any())
|
||||||
|
<div class="alert alert-danger">
|
||||||
|
<ul>
|
||||||
|
@foreach ($errors->all() as $error)
|
||||||
|
<li>{{ $error }}</li>
|
||||||
|
@endforeach
|
||||||
|
</ul>
|
||||||
|
</div>
|
||||||
|
@endif
|
||||||
|
</x-layout>
|
@ -1,3 +0,0 @@
|
|||||||
<button wire:loading.remove {{ $attributes }} class="btn btn-primary rounded-none btn-xs no-animation"> {{ $slot }} </button>
|
|
||||||
|
|
||||||
<button wire:loading class="btn btn-disabled rounded-none btn-xs no-animation"> {{ $slot }}</button>
|
|
@ -1,5 +1,5 @@
|
|||||||
<!DOCTYPE html>
|
<!DOCTYPE html>
|
||||||
<html lang="{{ str_replace('_', '-', app()->getLocale()) }}" data-theme="light" class="h-full">
|
<html lang="{{ str_replace('_', '-', app()->getLocale()) }}">
|
||||||
|
|
||||||
<head>
|
<head>
|
||||||
<meta charset="utf-8">
|
<meta charset="utf-8">
|
||||||
@ -10,11 +10,9 @@
|
|||||||
@livewireStyles
|
@livewireStyles
|
||||||
</head>
|
</head>
|
||||||
|
|
||||||
<body class="h-full">
|
<body>
|
||||||
@auth
|
<x-navbar />
|
||||||
<x-navbar />
|
<main>
|
||||||
@endauth
|
|
||||||
<main class="h-full pt-10 p-4">
|
|
||||||
{{ $slot }}
|
{{ $slot }}
|
||||||
</main>
|
</main>
|
||||||
|
|
||||||
|
@ -1,15 +1,15 @@
|
|||||||
<nav class="flex space-x-4 p-2 fixed right-0">
|
<nav>
|
||||||
@env('local')
|
<a href="/">Home</a>
|
||||||
<a href="/run-command">Run command</a>
|
@guest
|
||||||
{{-- <livewire:create-token /> --}}
|
<a href="/login">Login</a>
|
||||||
<a href="/debug">Debug</a>
|
<a href="/register">Register</a>
|
||||||
<a href="/debug/logs" target="_blank">Debug Logs</a>
|
@endguest
|
||||||
{{-- <a href="/debug/inertia">Debug(inertia)</a> --}}
|
@auth
|
||||||
@endenv
|
<a href="/demo">Demo</a>
|
||||||
<a href="/">Dashboard</a>
|
<a href="/profile">Profile</a>
|
||||||
<a href="/profile">Profile</a>
|
<form action="/logout" method="POST">
|
||||||
<form action="/logout" method="POST">
|
@csrf
|
||||||
@csrf
|
<button type="submit">Logout</button>
|
||||||
<button type="submit">Logout</button>
|
</form>
|
||||||
</form>
|
@endauth
|
||||||
</nav>
|
</nav>
|
||||||
|
@ -1,5 +1,3 @@
|
|||||||
<x-layout>
|
<x-layout>
|
||||||
|
|
||||||
<livewire:run-command />
|
<livewire:run-command />
|
||||||
|
|
||||||
</x-layout>
|
</x-layout>
|
||||||
|
10
resources/views/home.blade.php
Normal file
10
resources/views/home.blade.php
Normal file
@ -0,0 +1,10 @@
|
|||||||
|
<x-layout>
|
||||||
|
<h1>
|
||||||
|
Coolify v4 🎉
|
||||||
|
</h1>
|
||||||
|
@guest
|
||||||
|
<p>
|
||||||
|
To see the demo, please <a href='/login'>login<a> or <a href='/register'>register<a>.
|
||||||
|
</p>
|
||||||
|
@endguest
|
||||||
|
</x-layout>
|
@ -1,7 +1,8 @@
|
|||||||
<div>
|
<div>
|
||||||
<div>
|
<div>
|
||||||
<label for="command">
|
<label for="command">
|
||||||
<input class="py-2 rounded ring-1" id="command" wire:model="command" type="text" />
|
<input autofocus class="py-2 rounded ring-1" id="command" wire:model="command" type="text"
|
||||||
|
wire:keydown.enter="runCommand" />
|
||||||
</label>
|
</label>
|
||||||
<button wire:click="runCommand">Run command</button>
|
<button wire:click="runCommand">Run command</button>
|
||||||
|
|
||||||
@ -20,8 +21,7 @@
|
|||||||
<div>
|
<div>
|
||||||
Activity: <span>{{ $activity?->id ?? 'waiting' }}</span>
|
Activity: <span>{{ $activity?->id ?? 'waiting' }}</span>
|
||||||
</div>
|
</div>
|
||||||
<pre style="width: 100%;overflow-y: scroll;"
|
<pre style="width: 100%;overflow-y: scroll;" @if ($isKeepAliveOn || $manualKeepAlive) wire:poll.750ms="polling" @endif>{{ data_get($activity, 'description') }}</pre>
|
||||||
@if ($isKeepAliveOn || $manualKeepAlive) wire:poll.750ms="polling" @endif>{{ data_get($activity, 'description') }}</pre>
|
|
||||||
<div>
|
<div>
|
||||||
<div>Details:</div>
|
<div>Details:</div>
|
||||||
<pre style="width: 100%;overflow-y: scroll;">{{ json_encode(data_get($activity, 'properties'), JSON_PRETTY_PRINT) }}</pre>
|
<pre style="width: 100%;overflow-y: scroll;">{{ json_encode(data_get($activity, 'properties'), JSON_PRETTY_PRINT) }}</pre>
|
||||||
|
6
resources/views/livewire/switch-team.blade.php
Normal file
6
resources/views/livewire/switch-team.blade.php
Normal file
@ -0,0 +1,6 @@
|
|||||||
|
<div>
|
||||||
|
@foreach (auth()->user()->otherTeams() as $team)
|
||||||
|
<button wire:key="{{ $team->id }}" wire:click="switch_to('{{ $team->id }}')">Switch to:
|
||||||
|
{{ $team->name }}</button>
|
||||||
|
@endforeach
|
||||||
|
</div>
|
17
resources/views/profile.blade.php
Normal file
17
resources/views/profile.blade.php
Normal file
@ -0,0 +1,17 @@
|
|||||||
|
<x-layout>
|
||||||
|
<div>
|
||||||
|
<div>
|
||||||
|
<h3>User</h3>
|
||||||
|
<p>Name: {{ auth()->user()->name }}</p>
|
||||||
|
<p>Id: {{ auth()->user()->id }}</p>
|
||||||
|
<p>Uuid: {{ auth()->user()->uuid }}</p>
|
||||||
|
</div>
|
||||||
|
<div>
|
||||||
|
<h3>Current Team</h3>
|
||||||
|
<p>Name: {{ session('currentTeam')->name }}</p>
|
||||||
|
<p>Id: {{ session('currentTeam')->id }}</p>
|
||||||
|
<p>Uuid: {{ session('currentTeam')->uuid }}</p>
|
||||||
|
</div>
|
||||||
|
<livewire:switch-team>
|
||||||
|
</div>
|
||||||
|
</x-layout>
|
@ -1,22 +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>{{ $title ?? 'Coolify' }}</title>
|
|
||||||
<meta name="csrf-token" content="{{ csrf_token() }}">
|
|
||||||
@vite(['resources/js/app.js', 'resources/css/app.css'])
|
|
||||||
</head>
|
|
||||||
|
|
||||||
<body class="antialiased">
|
|
||||||
<h1 class="text-3xl font-bold">
|
|
||||||
Coolify v4
|
|
||||||
</h1>
|
|
||||||
|
|
||||||
<p class="mt-4">
|
|
||||||
<a href="/demo"> See demo </a>
|
|
||||||
</p>
|
|
||||||
</body>
|
|
||||||
|
|
||||||
</html>
|
|
@ -14,7 +14,14 @@ use Illuminate\Support\Facades\Route;
|
|||||||
*/
|
*/
|
||||||
|
|
||||||
Route::get('/', function () {
|
Route::get('/', function () {
|
||||||
return view('welcome');
|
return view('home');
|
||||||
});
|
});
|
||||||
|
|
||||||
Route::view('/demo', 'demo');
|
Route::middleware(['auth'])->group(function () {
|
||||||
|
Route::get('/profile', function () {
|
||||||
|
return view('profile');
|
||||||
|
});
|
||||||
|
Route::get('/demo', function () {
|
||||||
|
return view('demo');
|
||||||
|
});
|
||||||
|
});
|
||||||
|
Loading…
x
Reference in New Issue
Block a user