Fix redirect loop on session expiry and add missing nav links
- Add redirectGuestsTo in bootstrap/app.php so unauthenticated users always redirect to the full account login URL instead of a relative /login that would 404 on the admin subdomain or loop - Create HandleInertiaRequests middleware to share auth.user, flash messages, and domain config to all Vue pages (was entirely missing) - Add Profile nav link in AppLayout, "Customer View" link in AdminLayout - Point logout to account subdomain where Fortify routes live - Link AuthLayout logo back to marketing site - Fix Marketing/Home links to use full account subdomain URLs - Update RoleBasedAccessTest to match new redirect URL Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
30
website/app/Http/Middleware/HandleInertiaRequests.php
Normal file
30
website/app/Http/Middleware/HandleInertiaRequests.php
Normal file
@@ -0,0 +1,30 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
declare(strict_types=1);
|
||||||
|
|
||||||
|
namespace App\Http\Middleware;
|
||||||
|
|
||||||
|
use Illuminate\Http\Request;
|
||||||
|
use Inertia\Middleware;
|
||||||
|
|
||||||
|
class HandleInertiaRequests extends Middleware
|
||||||
|
{
|
||||||
|
/** @return array<string, mixed> */
|
||||||
|
public function share(Request $request): array
|
||||||
|
{
|
||||||
|
return array_merge(parent::share($request), [
|
||||||
|
'auth' => fn () => [
|
||||||
|
'user' => $request->user() ? $request->user()->only('id', 'name', 'email', 'status') : null,
|
||||||
|
],
|
||||||
|
'flash' => fn () => [
|
||||||
|
'success' => $request->session()->get('success'),
|
||||||
|
'error' => $request->session()->get('error'),
|
||||||
|
],
|
||||||
|
'domains' => fn () => [
|
||||||
|
'marketing' => config('app.domains.marketing'),
|
||||||
|
'account' => config('app.domains.account'),
|
||||||
|
'admin' => config('app.domains.admin'),
|
||||||
|
],
|
||||||
|
]);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -29,9 +29,11 @@ return Application::configure(basePath: dirname(__DIR__))
|
|||||||
$middleware->trustProxies(at: '*');
|
$middleware->trustProxies(at: '*');
|
||||||
|
|
||||||
$middleware->web(append: [
|
$middleware->web(append: [
|
||||||
\Inertia\Middleware::class,
|
\App\Http\Middleware\HandleInertiaRequests::class,
|
||||||
]);
|
]);
|
||||||
|
|
||||||
|
$middleware->redirectGuestsTo(fn () => 'https://'.config('app.domains.account').'/login');
|
||||||
|
|
||||||
$middleware->alias([
|
$middleware->alias([
|
||||||
'role' => \Spatie\Permission\Middleware\RoleMiddleware::class,
|
'role' => \Spatie\Permission\Middleware\RoleMiddleware::class,
|
||||||
'permission' => \Spatie\Permission\Middleware\PermissionMiddleware::class,
|
'permission' => \Spatie\Permission\Middleware\PermissionMiddleware::class,
|
||||||
|
|||||||
@@ -1,8 +1,12 @@
|
|||||||
<script setup>
|
<script setup>
|
||||||
import { Link, usePage } from '@inertiajs/vue3';
|
import { Link, usePage } from '@inertiajs/vue3';
|
||||||
|
import { computed } from 'vue';
|
||||||
|
import FlashMessages from '@/Components/FlashMessages.vue';
|
||||||
|
|
||||||
const page = usePage();
|
const page = usePage();
|
||||||
const user = page.props.auth?.user;
|
const user = computed(() => page.props.auth?.user);
|
||||||
|
const domains = computed(() => page.props.domains);
|
||||||
|
const accountUrl = computed(() => `https://${domains.value?.account}`);
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<template>
|
<template>
|
||||||
@@ -24,10 +28,17 @@ const user = page.props.auth?.user;
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="flex items-center space-x-4">
|
<div class="flex items-center space-x-4">
|
||||||
|
<a
|
||||||
|
v-if="user"
|
||||||
|
:href="accountUrl + '/dashboard'"
|
||||||
|
class="text-sm text-gray-400 hover:text-white"
|
||||||
|
>
|
||||||
|
Customer View
|
||||||
|
</a>
|
||||||
<span v-if="user" class="text-sm text-gray-300">{{ user.name }}</span>
|
<span v-if="user" class="text-sm text-gray-300">{{ user.name }}</span>
|
||||||
<Link
|
<Link
|
||||||
v-if="user"
|
v-if="user"
|
||||||
href="/logout"
|
:href="accountUrl + '/logout'"
|
||||||
method="post"
|
method="post"
|
||||||
as="button"
|
as="button"
|
||||||
class="text-sm text-gray-400 hover:text-white"
|
class="text-sm text-gray-400 hover:text-white"
|
||||||
@@ -40,6 +51,7 @@ const user = page.props.auth?.user;
|
|||||||
</nav>
|
</nav>
|
||||||
|
|
||||||
<main class="max-w-7xl mx-auto py-6 px-4 sm:px-6 lg:px-8">
|
<main class="max-w-7xl mx-auto py-6 px-4 sm:px-6 lg:px-8">
|
||||||
|
<FlashMessages />
|
||||||
<slot />
|
<slot />
|
||||||
</main>
|
</main>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@@ -1,8 +1,11 @@
|
|||||||
<script setup>
|
<script setup>
|
||||||
import { Link, usePage } from '@inertiajs/vue3';
|
import { Link, usePage } from '@inertiajs/vue3';
|
||||||
|
import { computed } from 'vue';
|
||||||
|
import FlashMessages from '@/Components/FlashMessages.vue';
|
||||||
|
|
||||||
const page = usePage();
|
const page = usePage();
|
||||||
const user = page.props.auth?.user;
|
const user = computed(() => page.props.auth?.user);
|
||||||
|
const domains = computed(() => page.props.domains);
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<template>
|
<template>
|
||||||
@@ -21,6 +24,12 @@ const user = page.props.auth?.user;
|
|||||||
>
|
>
|
||||||
Dashboard
|
Dashboard
|
||||||
</Link>
|
</Link>
|
||||||
|
<Link
|
||||||
|
href="/profile"
|
||||||
|
class="px-3 py-2 rounded-md text-sm font-medium text-gray-700 hover:text-gray-900 hover:bg-gray-100"
|
||||||
|
>
|
||||||
|
Profile
|
||||||
|
</Link>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="flex items-center space-x-4">
|
<div class="flex items-center space-x-4">
|
||||||
@@ -40,6 +49,7 @@ const user = page.props.auth?.user;
|
|||||||
</nav>
|
</nav>
|
||||||
|
|
||||||
<main class="max-w-7xl mx-auto py-6 px-4 sm:px-6 lg:px-8">
|
<main class="max-w-7xl mx-auto py-6 px-4 sm:px-6 lg:px-8">
|
||||||
|
<FlashMessages />
|
||||||
<slot />
|
<slot />
|
||||||
</main>
|
</main>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@@ -1,11 +1,17 @@
|
|||||||
<script setup>
|
<script setup>
|
||||||
|
import { usePage } from '@inertiajs/vue3';
|
||||||
|
import { computed } from 'vue';
|
||||||
|
|
||||||
|
const page = usePage();
|
||||||
|
const domains = computed(() => page.props.domains);
|
||||||
|
const marketingUrl = computed(() => `https://${domains.value?.marketing}`);
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<template>
|
<template>
|
||||||
<div class="min-h-screen flex flex-col items-center justify-center bg-gray-50 py-12 px-4 sm:px-6 lg:px-8">
|
<div class="min-h-screen flex flex-col items-center justify-center bg-gray-50 py-12 px-4 sm:px-6 lg:px-8">
|
||||||
<div class="w-full max-w-md">
|
<div class="w-full max-w-md">
|
||||||
<div class="text-center mb-8">
|
<div class="text-center mb-8">
|
||||||
<h1 class="text-3xl font-bold text-gray-900">EZSCALE</h1>
|
<a :href="marketingUrl" class="text-3xl font-bold text-gray-900">EZSCALE</a>
|
||||||
<p class="mt-2 text-sm text-gray-600">Cloud Hosting Platform</p>
|
<p class="mt-2 text-sm text-gray-600">Cloud Hosting Platform</p>
|
||||||
</div>
|
</div>
|
||||||
<div class="bg-white shadow-sm rounded-lg border border-gray-200 p-8">
|
<div class="bg-white shadow-sm rounded-lg border border-gray-200 p-8">
|
||||||
|
|||||||
@@ -1,4 +1,10 @@
|
|||||||
<script setup>
|
<script setup>
|
||||||
|
import { usePage } from '@inertiajs/vue3';
|
||||||
|
import { computed } from 'vue';
|
||||||
|
|
||||||
|
const page = usePage();
|
||||||
|
const domains = computed(() => page.props.domains);
|
||||||
|
const accountUrl = computed(() => `https://${domains.value?.account}`);
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<template>
|
<template>
|
||||||
@@ -8,8 +14,8 @@
|
|||||||
<div class="flex justify-between h-16 items-center">
|
<div class="flex justify-between h-16 items-center">
|
||||||
<span class="text-xl font-bold text-gray-900">EZSCALE</span>
|
<span class="text-xl font-bold text-gray-900">EZSCALE</span>
|
||||||
<div class="space-x-4">
|
<div class="space-x-4">
|
||||||
<a href="/login" class="text-sm text-gray-600 hover:text-gray-900">Sign in</a>
|
<a :href="accountUrl + '/login'" class="text-sm text-gray-600 hover:text-gray-900">Sign in</a>
|
||||||
<a href="/register" class="text-sm px-4 py-2 bg-blue-600 text-white rounded-md hover:bg-blue-700">Get Started</a>
|
<a :href="accountUrl + '/register'" class="text-sm px-4 py-2 bg-blue-600 text-white rounded-md hover:bg-blue-700">Get Started</a>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
@@ -25,7 +31,7 @@
|
|||||||
VPS, Dedicated Servers, Web Hosting, and Game Servers. Powered by EZSCALE.
|
VPS, Dedicated Servers, Web Hosting, and Game Servers. Powered by EZSCALE.
|
||||||
</p>
|
</p>
|
||||||
<div class="mt-10">
|
<div class="mt-10">
|
||||||
<a href="/register" class="px-8 py-3 bg-blue-600 text-white font-medium rounded-md hover:bg-blue-700 text-lg">
|
<a :href="accountUrl + '/register'" class="px-8 py-3 bg-blue-600 text-white font-medium rounded-md hover:bg-blue-700 text-lg">
|
||||||
Start Free Trial
|
Start Free Trial
|
||||||
</a>
|
</a>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@@ -12,7 +12,7 @@ beforeEach(function (): void {
|
|||||||
|
|
||||||
it('redirects unauthenticated users to login', function (): void {
|
it('redirects unauthenticated users to login', function (): void {
|
||||||
$this->get($this->accountUrl.'/dashboard')
|
$this->get($this->accountUrl.'/dashboard')
|
||||||
->assertRedirect($this->accountUrl.'/login');
|
->assertRedirect('https://'.config('app.domains.account').'/login');
|
||||||
});
|
});
|
||||||
|
|
||||||
it('identifies admin users correctly', function (): void {
|
it('identifies admin users correctly', function (): void {
|
||||||
|
|||||||
Reference in New Issue
Block a user