fixing routes

This commit is contained in:
Saeed Vaziry 2024-10-07 00:18:11 +02:00
parent a94d1d42d2
commit 8bf1cc141e
22 changed files with 70 additions and 522 deletions

View File

@ -18,6 +18,8 @@ public function update(User $user, array $input): void
$user->save();
}
$user->refresh();
/** @var Project $firstProject */
$firstProject = $user->projects->first();
if (! $user->currentProject && $firstProject) {

View File

@ -1,19 +0,0 @@
<?php
namespace App\Facades;
use Illuminate\Support\Facades\Facade;
/**
* @method static void success(string $message)
* @method static void error(string $message)
* @method static void warning(string $message)
* @method static void info(string $message)
*/
class Toast extends Facade
{
protected static function getFacadeAccessor(): string
{
return 'toast';
}
}

View File

@ -1,32 +0,0 @@
<?php
namespace App\Helpers;
class Toast
{
public function success(string $message): void
{
$this->toast('success', $message);
}
public function error(string $message): void
{
$this->toast('error', $message);
}
public function warning(string $message): void
{
$this->toast('warning', $message);
}
public function info(string $message): void
{
$this->toast('info', $message);
}
private function toast(string $type, string $message): void
{
session()->flash('toast.type', $type);
session()->flash('toast.message', $message);
}
}

View File

@ -5,6 +5,8 @@
use App\Enums\UserRole;
use App\Traits\HasTimezoneTimestamps;
use Carbon\Carbon;
use Filament\Models\Contracts\FilamentUser;
use Filament\Panel;
use Illuminate\Database\Eloquent\Builder;
use Illuminate\Database\Eloquent\Factories\HasFactory;
use Illuminate\Database\Eloquent\Relations\BelongsToMany;
@ -39,7 +41,7 @@
* @property Carbon $created_at
* @property Carbon $updated_at
*/
class User extends Authenticatable
class User extends Authenticatable implements FilamentUser
{
use HasFactory;
use HasTimezoneTimestamps;
@ -157,4 +159,9 @@ public function allServers(): Builder
});
});
}
public function canAccessPanel(Panel $panel): bool
{
return true;
}
}

View File

@ -31,6 +31,6 @@ public function update(User $user, User $model): bool
public function delete(User $user, User $model): bool
{
return $user->isAdmin();
return $user->isAdmin() && $user->id !== $model->id;
}
}

View File

@ -5,10 +5,9 @@
use App\Helpers\FTP;
use App\Helpers\Notifier;
use App\Helpers\SSH;
use App\Helpers\Toast;
use Illuminate\Contracts\Container\BindingResolutionException;
use Illuminate\Http\Resources\Json\ResourceCollection;
use Illuminate\Support\ServiceProvider;
use Laravel\Fortify\Fortify;
class AppServiceProvider extends ServiceProvider
{
@ -17,12 +16,9 @@ class AppServiceProvider extends ServiceProvider
*/
public function register(): void
{
//
Fortify::ignoreRoutes();
}
/**
* @throws BindingResolutionException
*/
public function boot(): void
{
ResourceCollection::withoutWrapping();
@ -34,9 +30,6 @@ public function boot(): void
$this->app->bind('notifier', function () {
return new Notifier;
});
$this->app->bind('toast', function () {
return new Toast;
});
$this->app->bind('ftp', function () {
return new FTP;
});

View File

@ -1,63 +0,0 @@
<?php
namespace App\Providers;
use App\Actions\User\ResetUserPassword;
use Illuminate\Cache\RateLimiting\Limit;
use Illuminate\Http\Request;
use Illuminate\Support\Facades\RateLimiter;
use Illuminate\Support\ServiceProvider;
use Illuminate\Support\Str;
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::resetUserPasswordsUsing(ResetUserPassword::class);
RateLimiter::for('login', function (Request $request) {
$throttleKey = Str::transliterate(Str::lower($request->input(Fortify::username())).'|'.$request->ip());
return Limit::perMinute(5)->by($throttleKey);
});
RateLimiter::for('two-factor', function (Request $request) {
return Limit::perMinute(5)->by($request->session()->get('login.id'));
});
Fortify::loginView(function () {
return view('auth.login');
});
Fortify::requestPasswordResetLinkView(function () {
return view('auth.forgot-password');
});
Fortify::resetPasswordView(function (Request $request) {
return view('auth.reset-password', [
'token' => $request->route('token'),
'email' => $request->query('email'),
]);
});
Fortify::confirmPasswordView(function () {
return view('auth.confirm-password');
});
Fortify::twoFactorChallengeView(function () {
return view('auth.two-factor-challenge');
});
}
}

View File

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

View File

@ -80,7 +80,7 @@ public function panel(Panel $panel): Panel
return $panel
->default()
->id('app')
->path('app')
->path('')
->passwordReset()
->colors([
'primary' => Color::Indigo,

View File

@ -38,7 +38,7 @@ public function getWidgets(): array
protected function getHeaderActions(): array
{
return [
CreateAction::make()
CreateAction::make('create')
->label('Create User')
->icon('heroicon-o-plus')
->authorize('create', User::class)

View File

@ -87,13 +87,6 @@ public function getTable(): Table
->label('Projects')
->icon('heroicon-o-rectangle-stack')
->authorize(fn ($record) => auth()->user()->can('update', $record))
->action(function ($record, array $data) {
app(UpdateProjects::class)->update($record, $data);
Notification::make()
->title('Projects Updated')
->success()
->send();
})
->form(function (Form $form, $record) {
return $form
->schema([
@ -105,10 +98,17 @@ public function getTable(): Table
])
->columns(1);
})
->action(function ($record, array $data) {
app(UpdateProjects::class)->update($record, $data);
Notification::make()
->title('Projects Updated')
->success()
->send();
})
->modalSubmitActionLabel('Save')
->modalWidth(MaxWidth::Large),
DeleteAction::make()
->authorize(fn ($record) => auth()->user()->can('delete', $record)),
DeleteAction::make('delete')
->authorize(fn (User $record) => auth()->user()->can('delete', $record)),
]);
}
}

View File

@ -185,7 +185,6 @@
/*
* Package Service Providers...
*/
App\Providers\FortifyServiceProvider::class,
/*
* Application Service Providers...

View File

@ -2,6 +2,7 @@
return [
'path' => '/',
/*
|--------------------------------------------------------------------------
| Broadcasting

View File

@ -1,147 +0,0 @@
<?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,
]),
],
];

View File

@ -3,10 +3,6 @@
use App\Http\Controllers\ConsoleController;
use Illuminate\Support\Facades\Route;
Route::get('/', function () {
return redirect()->route('login');
});
Route::middleware('auth')->group(function () {
Route::post('/{server}/console', [ConsoleController::class, 'run'])->name('servers.console.run');
});

View File

@ -200,8 +200,9 @@ echo "* * * * * cd /home/${V_USERNAME}/vito && php artisan schedule:run >> /dev/
# cleanup
chown -R ${V_USERNAME}:${V_USERNAME} /home/${V_USERNAME}
# cache
php artisan config:cache
# optimize
php artisan optimize
php artisan icons:cache
# start worker
supervisorctl start worker:*

View File

@ -6,7 +6,7 @@ echo "Pulling changes..."
git fetch --all
echo "Checking out the latest tag..."
NEW_RELEASE=$(git tag -l "1.*" --sort=-v:refname | head -n 1)
NEW_RELEASE=$(git tag -l "2.*" --sort=-v:refname | head -n 1)
git checkout "$NEW_RELEASE"
git pull origin "$NEW_RELEASE"

View File

@ -1,47 +0,0 @@
<?php
namespace Tests\Feature\Auth;
use App\Providers\RouteServiceProvider;
use Illuminate\Foundation\Testing\RefreshDatabase;
use Tests\TestCase;
class AuthenticationTest extends TestCase
{
use RefreshDatabase;
public function test_login_screen_can_be_rendered(): void
{
$response = $this->get('/login');
$response->assertStatus(200);
}
public function test_users_can_authenticate_using_the_login_screen(): void
{
$response = $this->post('/login', [
'email' => $this->user->email,
'password' => 'password',
]);
$this->assertAuthenticated();
$response->assertRedirect(RouteServiceProvider::HOME);
}
public function test_users_can_not_authenticate_with_invalid_password(): void
{
$this->post('/login', [
'email' => $this->user->email,
'password' => 'wrong-password',
]);
$this->assertGuest();
}
public function test_redirect_if_not_authenticated(): void
{
$response = $this->get('/servers');
$response->assertRedirect('/login');
}
}

View File

@ -1,29 +0,0 @@
<?php
namespace Tests\Feature\Auth;
use Illuminate\Foundation\Testing\RefreshDatabase;
use Tests\TestCase;
class LogoutTest extends TestCase
{
use RefreshDatabase;
public function test_logout(): void
{
$this->actingAs($this->user);
$this->post(route('logout'))->assertRedirect('/');
$this->assertFalse(auth()->check());
}
public function test_user_still_logged_in(): void
{
$this->actingAs($this->user);
$this->get(route('login'))->assertRedirect(route('servers'));
$this->assertTrue(auth()->check());
}
}

View File

@ -1,43 +0,0 @@
<?php
namespace Tests\Feature\Auth;
use Illuminate\Foundation\Testing\RefreshDatabase;
use Tests\TestCase;
class PasswordConfirmationTest extends TestCase
{
use RefreshDatabase;
public function test_confirm_password_screen_can_be_rendered(): void
{
$this->actingAs($this->user);
$response = $this->get(route('password.confirm'));
$response->assertStatus(200);
}
public function test_password_can_be_confirmed(): void
{
$this->actingAs($this->user);
$response = $this->post(route('password.confirm'), [
'password' => 'password',
]);
$response->assertRedirect();
$response->assertSessionDoesntHaveErrors();
}
public function test_password_is_not_confirmed_with_invalid_password(): void
{
$this->actingAs($this->user);
$response = $this->post(route('password.confirm'), [
'password' => 'wrong-password',
]);
$response->assertSessionHasErrors();
}
}

View File

@ -1,64 +0,0 @@
<?php
namespace Tests\Feature\Auth;
use Illuminate\Auth\Notifications\ResetPassword;
use Illuminate\Foundation\Testing\RefreshDatabase;
use Illuminate\Support\Facades\Notification;
use Tests\TestCase;
class PasswordResetTest extends TestCase
{
use RefreshDatabase;
public function test_reset_password_link_screen_can_be_rendered(): void
{
$response = $this->get('/forgot-password');
$response->assertStatus(200);
}
public function test_reset_password_link_can_be_requested(): void
{
Notification::fake();
$this->post('/forgot-password', ['email' => $this->user->email]);
Notification::assertSentTo($this->user, ResetPassword::class);
}
public function test_reset_password_screen_can_be_rendered(): void
{
Notification::fake();
$this->post('/forgot-password', ['email' => $this->user->email]);
Notification::assertSentTo($this->user, ResetPassword::class, function ($notification) {
$response = $this->get('/reset-password/'.$notification->token);
$response->assertStatus(200);
return true;
});
}
public function test_password_can_be_reset_with_valid_token(): void
{
Notification::fake();
$this->post('/forgot-password', ['email' => $this->user->email]);
Notification::assertSentTo($this->user, ResetPassword::class, function ($notification) {
$response = $this->post('/reset-password', [
'token' => $notification->token,
'email' => $this->user->email,
'password' => 'password',
'password_confirmation' => 'password',
]);
$response->assertSessionDoesntHaveErrors();
return true;
});
}
}

View File

@ -5,7 +5,10 @@
use App\Enums\UserRole;
use App\Models\Project;
use App\Models\User;
use App\Web\Pages\Settings\Users\Index;
use App\Web\Pages\Settings\Users\Widgets\UsersList;
use Illuminate\Foundation\Testing\RefreshDatabase;
use Livewire\Livewire;
use Tests\TestCase;
class UserTest extends TestCase
@ -16,12 +19,13 @@ public function test_create_user(): void
{
$this->actingAs($this->user);
$this->post(route('settings.users.store'), [
Livewire::test(Index::class)
->callAction('create', [
'name' => 'new user',
'email' => 'newuser@example.com',
'password' => 'password',
'role' => UserRole::USER,
])->assertSessionDoesntHaveErrors();
]);
$this->assertDatabaseHas('users', [
'name' => 'new user',
@ -36,9 +40,11 @@ public function test_see_users_list(): void
$user = User::factory()->create();
$this->get(route('settings.users.index'))
->assertSuccessful()
->assertSee($user->name);
$this->get(Index::getUrl())
->assertSuccessful();
Livewire::test(UsersList::class)
->assertCanSeeTableRecords([$user]);
}
public function test_must_be_admin_to_see_users_list(): void
@ -48,9 +54,7 @@ public function test_must_be_admin_to_see_users_list(): void
$this->actingAs($this->user);
$user = User::factory()->create();
$this->get(route('settings.users.index'))
$this->get(Index::getUrl())
->assertForbidden();
}
@ -60,8 +64,8 @@ public function test_delete_user(): void
$user = User::factory()->create();
$this->delete(route('settings.users.delete', $user))
->assertSessionDoesntHaveErrors();
Livewire::test(UsersList::class)
->callTableAction('delete', $user);
$this->assertDatabaseMissing('users', [
'id' => $user->id,
@ -72,24 +76,8 @@ public function test_cannot_delete_yourself(): void
{
$this->actingAs($this->user);
$this->delete(route('settings.users.delete', $this->user))
->assertSessionDoesntHaveErrors()
->assertSessionHas('toast.type', 'error');
$this->assertDatabaseHas('users', [
'id' => $this->user->id,
]);
}
public function test_see_user(): void
{
$this->actingAs($this->user);
$user = User::factory()->create();
$this->get(route('settings.users.show', $user))
->assertSuccessful()
->assertSee($user->name);
Livewire::test(UsersList::class)
->assertTableActionHidden('delete', $this->user);
}
public function test_edit_user_info(): void
@ -98,13 +86,14 @@ public function test_edit_user_info(): void
$user = User::factory()->create();
$this->post(route('settings.users.update', $user), [
Livewire::test(UsersList::class)
->callTableAction('edit', $user, [
'name' => 'new-name',
'email' => 'newemail@example.com',
'timezone' => 'Europe/London',
'role' => UserRole::ADMIN,
])
->assertSessionDoesntHaveErrors();
->assertSuccessful();
$this->assertDatabaseHas('users', [
'id' => $user->id,
@ -122,10 +111,11 @@ public function test_edit_user_projects(): void
$user = User::factory()->create();
$project = Project::factory()->create();
$this->post(route('settings.users.update-projects', $user), [
Livewire::test(UsersList::class)
->callTableAction('update-projects', $user, [
'projects' => [$project->id],
])
->assertSessionDoesntHaveErrors();
->assertSuccessful();
$this->assertDatabaseHas('user_project', [
'user_id' => $user->id,
@ -137,16 +127,19 @@ public function test_edit_user_projects_with_current_project(): void
{
$this->actingAs($this->user);
/** @var User $user */
$user = User::factory()->create();
$user->current_project_id = null;
$user->save();
/** @var Project $project */
$project = Project::factory()->create();
$this->post(route('settings.users.update-projects', $user), [
Livewire::test(UsersList::class)
->callTableAction('update-projects', $user, [
'projects' => [$project->id],
])
->assertSessionDoesntHaveErrors();
->assertSuccessful();
$this->assertDatabaseHas('user_project', [
'user_id' => $user->id,