This commit is contained in:
Adrian Hopek 2022-01-13 09:08:36 +01:00
parent 197e7da9cc
commit 38af769f7b
17 changed files with 1434 additions and 144 deletions

View File

@ -6,9 +6,9 @@ namespace Toby\Http\Controllers;
use Illuminate\Contracts\Auth\Factory as AuthFactory;
use Illuminate\Database\Eloquent\ModelNotFoundException;
use Toby\Models\User;
use Laravel\Socialite\SocialiteManager;
use Symfony\Component\HttpFoundation\RedirectResponse;
use Toby\Models\User;
class GoogleController extends Controller
{
@ -29,8 +29,6 @@ class GoogleController extends Controller
$auth->guard()->login($user, true);
$user->syncGoogleData($socialUser);
return redirect()->route("dashboard");
} catch (ModelNotFoundException) {
return redirect()

View File

@ -0,0 +1,22 @@
<?php
declare(strict_types=1);
namespace Toby\Http\Controllers;
use Illuminate\Http\Request;
use Illuminate\Support\Facades\Auth;
use Symfony\Component\HttpFoundation\RedirectResponse;
class LogoutController extends Controller
{
public function __invoke(Request $request): RedirectResponse
{
Auth::logout();
$request->session()->invalidate();
$request->session()->regenerateToken();
return redirect()->route("login");
}
}

View File

@ -6,6 +6,7 @@ namespace Toby\Http\Middleware;
use Illuminate\Http\Request;
use Inertia\Middleware;
use Toby\Http\Resources\UserResource;
class HandleInertiaRequests extends Middleware
{
@ -15,12 +16,7 @@ class HandleInertiaRequests extends Middleware
return array_merge(parent::share($request), [
"auth" => fn() => [
"user" => $user ? [
"name" => $user->name,
"email" => $user->email,
"role" => "Human Resources Manager",
"imageUrl" => $user->avatar,
] : null,
"user" => $user ? new UserResource($user) : null,
],
"flash" => fn() => [
"success" => $request->session()->get("success"),

View File

@ -0,0 +1,21 @@
<?php
declare(strict_types=1);
namespace Toby\Http\Resources;
use Illuminate\Http\Resources\Json\JsonResource;
class UserResource extends JsonResource
{
public function toArray($request): array
{
return [
"id" => $this->id,
"name" => $this->name,
"email" => $this->email,
"role" => "Human Resources Manager",
"avatar" => "https://images.unsplash.com/photo-1550525811-e5869dd03032?ixlib=rb-1.2.1&ixid=eyJhcHBfaWQiOjEyMDd9&auto=format&fit=facearea&facepad=2&w=256&h=256&q=80",
];
}
}

View File

@ -7,14 +7,11 @@ namespace Toby\Models;
use Illuminate\Database\Eloquent\Factories\HasFactory;
use Illuminate\Foundation\Auth\User as Authenticatable;
use Illuminate\Notifications\Notifiable;
use Laravel\Socialite\Two\User as SocialUser;
/**
* @property int $id
* @property string $name
* @property string $email
* @property string $avatar
* @property string $google_id
*/
class User extends Authenticatable
{
@ -29,17 +26,4 @@ class User extends Authenticatable
protected $hidden = [
"remember_token",
];
protected $casts = [
"email_verified_at" => "datetime",
];
public function syncGoogleData(SocialUser $user): void
{
$this->name = $user->getName();
$this->avatar = $user->getAvatar();
$this->google_id = $user->getId();
$this->save();
}
}

1353
composer.lock generated

File diff suppressed because it is too large Load Diff

View File

@ -12,9 +12,7 @@ return new class() extends Migration {
Schema::create("users", function (Blueprint $table): void {
$table->id();
$table->string("name");
$table->string("avatar")->nullable();
$table->string("email")->unique();
$table->string("google_id")->nullable();
$table->rememberToken();
$table->timestamps();
});

60
package-lock.json generated
View File

@ -1,5 +1,5 @@
{
"name": "application",
"name": "toby",
"lockfileVersion": 2,
"requires": true,
"packages": {
@ -11,15 +11,15 @@
"@inertiajs/inertia-vue3": "^0.6.0",
"@inertiajs/progress": "^0.2.7",
"@tailwindcss/forms": "^0.4.0",
"@tailwindcss/line-clamp": "^0.3.0",
"@tailwindcss/line-clamp": "^0.3.1",
"@tailwindcss/typography": "^0.5.0",
"@vue/compiler-sfc": "^3.2.26",
"autoprefixer": "^10.4.2",
"laravel-mix": "^6.0.6",
"postcss": "^8.4.5",
"tailwindcss": "^3.0.12",
"tailwindcss": "^3.0.13",
"vue": "^3.2.26",
"vue-loader": "^16.8.3"
"vue-loader": "^17.0.0"
},
"devDependencies": {
"eslint": "^8.6.0",
@ -1782,9 +1782,9 @@
}
},
"node_modules/@tailwindcss/line-clamp": {
"version": "0.3.0",
"resolved": "https://registry.npmjs.org/@tailwindcss/line-clamp/-/line-clamp-0.3.0.tgz",
"integrity": "sha512-ffDDclrqr3sy8cpChCozedDUAN8enxqAiWeH8d4dGQ2hcXlxf51+7KleveFi/n/TxEuRVApoL7hICeDOdYPKpg==",
"version": "0.3.1",
"resolved": "https://registry.npmjs.org/@tailwindcss/line-clamp/-/line-clamp-0.3.1.tgz",
"integrity": "sha512-pNr0T8LAc3TUx/gxCfQZRe9NB2dPEo/cedPHzUGIPxqDMhgjwNm6jYxww4W5l0zAsAddxr+XfZcqttGiFDgrGg==",
"peerDependencies": {
"tailwindcss": ">=2.0.0 || >=3.0.0 || >=3.0.0-alpha.1"
}
@ -4166,9 +4166,9 @@
}
},
"node_modules/eslint-plugin-vue": {
"version": "8.2.0",
"resolved": "https://registry.npmjs.org/eslint-plugin-vue/-/eslint-plugin-vue-8.2.0.tgz",
"integrity": "sha512-cLIdTuOAMXyHeQ4drYKcZfoyzdwdBpH279X8/N0DgmotEI9yFKb5O/cAgoie/CkQZCH/MOmh0xw/KEfS90zY2A==",
"version": "8.3.0",
"resolved": "https://registry.npmjs.org/eslint-plugin-vue/-/eslint-plugin-vue-8.3.0.tgz",
"integrity": "sha512-IIuLHw4vQxGlHcoP2dG6t/2OVdQf2qoyAzEGAxreU1afZOHGA7y3TWq8I+r3ZA6Wjs6xpeUWGHlT31QGr9Rb5g==",
"dev": true,
"dependencies": {
"eslint-utils": "^3.0.0",
@ -8451,9 +8451,9 @@
}
},
"node_modules/tailwindcss": {
"version": "3.0.12",
"resolved": "https://registry.npmjs.org/tailwindcss/-/tailwindcss-3.0.12.tgz",
"integrity": "sha512-VqhF86z2c34sJyS5ZS8Q2nYuN0KzqZw1GGsuQQO9kJ3mY1oG7Fsag0vICkxUVXk6P+1sUkTkjMjKWCjEF0hNHw==",
"version": "3.0.13",
"resolved": "https://registry.npmjs.org/tailwindcss/-/tailwindcss-3.0.13.tgz",
"integrity": "sha512-raRPGFwQSGXn/3h0ttHND9jyPYfqk/ur2NXtlQuK25+ZnrCjlH1s1j4/oPswHGMoZzGNykUVycZ/LcROanUE0A==",
"dependencies": {
"arg": "^5.0.1",
"chalk": "^4.1.2",
@ -8474,7 +8474,7 @@
"postcss-selector-parser": "^6.0.8",
"postcss-value-parser": "^4.2.0",
"quick-lru": "^5.1.1",
"resolve": "^1.20.0"
"resolve": "^1.21.0"
},
"bin": {
"tailwind": "lib/cli.js",
@ -8893,9 +8893,9 @@
}
},
"node_modules/vue-loader": {
"version": "16.8.3",
"resolved": "https://registry.npmjs.org/vue-loader/-/vue-loader-16.8.3.tgz",
"integrity": "sha512-7vKN45IxsKxe5GcVCbc2qFU5aWzyiLrYJyUuMz4BQLKctCj/fmCa0w6fGiiQ2cLFetNcek1ppGJQDCup0c1hpA==",
"version": "17.0.0",
"resolved": "https://registry.npmjs.org/vue-loader/-/vue-loader-17.0.0.tgz",
"integrity": "sha512-OWSXjrzIvbF2LtOUmxT3HYgwwubbfFelN8PAP9R9dwpIkj48TVioHhWWSx7W7fk+iF5cgg3CBJRxwTdtLU4Ecg==",
"dependencies": {
"chalk": "^4.1.0",
"hash-sum": "^2.0.0",
@ -10639,9 +10639,9 @@
}
},
"@tailwindcss/line-clamp": {
"version": "0.3.0",
"resolved": "https://registry.npmjs.org/@tailwindcss/line-clamp/-/line-clamp-0.3.0.tgz",
"integrity": "sha512-ffDDclrqr3sy8cpChCozedDUAN8enxqAiWeH8d4dGQ2hcXlxf51+7KleveFi/n/TxEuRVApoL7hICeDOdYPKpg==",
"version": "0.3.1",
"resolved": "https://registry.npmjs.org/@tailwindcss/line-clamp/-/line-clamp-0.3.1.tgz",
"integrity": "sha512-pNr0T8LAc3TUx/gxCfQZRe9NB2dPEo/cedPHzUGIPxqDMhgjwNm6jYxww4W5l0zAsAddxr+XfZcqttGiFDgrGg==",
"requires": {}
},
"@tailwindcss/typography": {
@ -12614,9 +12614,9 @@
}
},
"eslint-plugin-vue": {
"version": "8.2.0",
"resolved": "https://registry.npmjs.org/eslint-plugin-vue/-/eslint-plugin-vue-8.2.0.tgz",
"integrity": "sha512-cLIdTuOAMXyHeQ4drYKcZfoyzdwdBpH279X8/N0DgmotEI9yFKb5O/cAgoie/CkQZCH/MOmh0xw/KEfS90zY2A==",
"version": "8.3.0",
"resolved": "https://registry.npmjs.org/eslint-plugin-vue/-/eslint-plugin-vue-8.3.0.tgz",
"integrity": "sha512-IIuLHw4vQxGlHcoP2dG6t/2OVdQf2qoyAzEGAxreU1afZOHGA7y3TWq8I+r3ZA6Wjs6xpeUWGHlT31QGr9Rb5g==",
"dev": true,
"requires": {
"eslint-utils": "^3.0.0",
@ -15646,9 +15646,9 @@
}
},
"tailwindcss": {
"version": "3.0.12",
"resolved": "https://registry.npmjs.org/tailwindcss/-/tailwindcss-3.0.12.tgz",
"integrity": "sha512-VqhF86z2c34sJyS5ZS8Q2nYuN0KzqZw1GGsuQQO9kJ3mY1oG7Fsag0vICkxUVXk6P+1sUkTkjMjKWCjEF0hNHw==",
"version": "3.0.13",
"resolved": "https://registry.npmjs.org/tailwindcss/-/tailwindcss-3.0.13.tgz",
"integrity": "sha512-raRPGFwQSGXn/3h0ttHND9jyPYfqk/ur2NXtlQuK25+ZnrCjlH1s1j4/oPswHGMoZzGNykUVycZ/LcROanUE0A==",
"requires": {
"arg": "^5.0.1",
"chalk": "^4.1.2",
@ -15669,7 +15669,7 @@
"postcss-selector-parser": "^6.0.8",
"postcss-value-parser": "^4.2.0",
"quick-lru": "^5.1.1",
"resolve": "^1.20.0"
"resolve": "^1.21.0"
},
"dependencies": {
"glob-parent": {
@ -15971,9 +15971,9 @@
}
},
"vue-loader": {
"version": "16.8.3",
"resolved": "https://registry.npmjs.org/vue-loader/-/vue-loader-16.8.3.tgz",
"integrity": "sha512-7vKN45IxsKxe5GcVCbc2qFU5aWzyiLrYJyUuMz4BQLKctCj/fmCa0w6fGiiQ2cLFetNcek1ppGJQDCup0c1hpA==",
"version": "17.0.0",
"resolved": "https://registry.npmjs.org/vue-loader/-/vue-loader-17.0.0.tgz",
"integrity": "sha512-OWSXjrzIvbF2LtOUmxT3HYgwwubbfFelN8PAP9R9dwpIkj48TVioHhWWSx7W7fk+iF5cgg3CBJRxwTdtLU4Ecg==",
"requires": {
"chalk": "^4.1.0",
"hash-sum": "^2.0.0",

View File

@ -18,15 +18,15 @@
"@inertiajs/inertia-vue3": "^0.6.0",
"@inertiajs/progress": "^0.2.7",
"@tailwindcss/forms": "^0.4.0",
"@tailwindcss/line-clamp": "^0.3.0",
"@tailwindcss/line-clamp": "^0.3.1",
"@tailwindcss/typography": "^0.5.0",
"@vue/compiler-sfc": "^3.2.26",
"autoprefixer": "^10.4.2",
"laravel-mix": "^6.0.6",
"postcss": "^8.4.5",
"tailwindcss": "^3.0.12",
"tailwindcss": "^3.0.13",
"vue": "^3.2.26",
"vue-loader": "^16.8.3"
"vue-loader": "^17.0.0"
},
"devDependencies": {
"eslint": "^8.6.0",

View File

@ -22,7 +22,7 @@
<server name="APP_KEY" value="base64:SKEJSy9oF9chQBCMbxqgj5zhtAvug9kwZ+cDiP1Y8A8="/>
<server name="BCRYPT_ROUNDS" value="4"/>
<server name="CACHE_DRIVER" value="array"/>
<server name="DB_CONNECTION" value="sqlite"/>
<server name="DB_CONNECTION" value="testing"/>
<server name="DB_DATABASE" value=":memory:"/>
<server name="MAIL_MAILER" value="array"/>
<server name="QUEUE_CONNECTION" value="sync"/>

BIN
public/img/icon.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 6.7 KiB

BIN
public/img/logo.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 6.9 KiB

View File

@ -18,7 +18,7 @@
<div class="flex-shrink-0">
<img
class="mx-auto h-20 w-20 rounded-full"
:src="user.imageUrl"
:src="user.avatar"
alt=""
>
</div>

View File

@ -42,7 +42,7 @@
<span class="sr-only">Open user menu</span>
<img
class="h-8 w-8 rounded-full"
:src="user.imageUrl"
:src="user.avatar"
alt=""
>
</MenuButton>
@ -62,7 +62,9 @@
>
<InertiaLink
:href="item.href"
:class="[active ? 'bg-gray-100' : '', 'block px-4 py-2 text-sm text-gray-700']"
:method="item.method"
:as="item.as"
:class="[active ? 'bg-gray-100' : '', 'block w-full text-left px-4 py-2 text-sm text-gray-700']"
>
{{ item.name }}
</InertiaLink>
@ -209,8 +211,10 @@
<InertiaLink
v-for="item in userNavigation"
:key="item.name"
:method="item.method"
:as="item.as"
:href="item.href"
class="block rounded-md px-3 py-2 text-base text-gray-900 font-medium hover:bg-gray-100 hover:text-gray-800"
class="block w-full text-left rounded-md px-3 py-2 text-base text-gray-900 font-medium hover:bg-gray-100 hover:text-gray-800"
>
{{ item.name }}
</InertiaLink>
@ -270,7 +274,7 @@ export default {
const userNavigation = [
{name: 'Your Profile', href: '#'},
{name: 'Settings', href: '#'},
{name: 'Sign out', href: '#'},
{name: 'Sign out', href: '/logout', method: 'post', as: 'button'},
];
return {

View File

@ -2,29 +2,19 @@
declare(strict_types=1);
use Illuminate\Http\Request;
use Illuminate\Support\Facades\Auth;
use Illuminate\Support\Facades\Route;
use Toby\Http\Controllers\GoogleController;
use Toby\Http\Controllers\LogoutController;
Route::middleware("auth")->group(function (): void {
Route::get("/", fn() => inertia("Dashboard"))->name("dashboard");
Route::get("/logout", function (Request $request) {
Auth::logout();
$request->session()->invalidate();
$request->session()->regenerateToken();
return redirect()->route("login");
});
Route::post("/logout", LogoutController::class);
});
Route::middleware("guest")->group(function(): void {
Route::middleware("guest")->group(function (): void {
Route::get("login", fn() => inertia("Login"))->name("login");
Route::get("login/google/start", [GoogleController::class, "redirect"])
->name("login.google.start");
Route::get("login/google/end", [GoogleController::class, "callback"])
->name("login.google.end");
});
});

View File

@ -0,0 +1,43 @@
<?php
declare(strict_types=1);
namespace Tests\Feature;
use Illuminate\Foundation\Testing\DatabaseMigrations;
use Tests\TestCase;
use Toby\Models\User;
class AuthenticationTest extends TestCase
{
use DatabaseMigrations;
public function testGuestIsRedirectedFromDashboard(): void
{
$this->get("/")
->assertRedirect();
}
public function testUserIsNotRedirectedFromDashboard(): void
{
$user = User::factory()->create();
$this->actingAs($user)
->get("/")
->assertOk();
}
public function testUserCanLogout(): void
{
$user = User::factory()->create();
$this->actingAs($user);
$this->assertAuthenticated();
$this->post("/logout")
->assertRedirect();
$this->assertGuest();
}
}

View File

@ -4,14 +4,21 @@ declare(strict_types=1);
namespace Tests\Feature;
use Illuminate\Foundation\Testing\DatabaseMigrations;
use Inertia\Testing\AssertableInertia as Assert;
use Tests\TestCase;
use Toby\Models\User;
class InertiaTest extends TestCase
{
use DatabaseMigrations;
public function testInertia(): void
{
$response = $this->get("/");
$user = User::factory()->create();
$response = $this->actingAs($user)
->get("/");
$response->assertOk();
$response->assertInertia(fn(Assert $page) => $page->component("Dashboard"));