#2 - authentication via google (#16)

* #2 - wip

* #2 - wip

* #2 - ui fixes to login page

* #2 - fix

* #2 - fix

* #2 - add title to login page

* Apply suggestions from code review

Co-authored-by: Krzysztof Rewak <krzysztof.rewak@blumilk.pl>

* #2 - cr fix

* #2 - cr fix

Co-authored-by: EwelinaLasowy <ewelina.lasowy@blumilk.pl>
Co-authored-by: Krzysztof Rewak <krzysztof.rewak@blumilk.pl>
This commit is contained in:
Adrian Hopek 2022-01-13 14:19:11 +01:00 committed by GitHub
parent e015f02892
commit 0869395aab
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
31 changed files with 534 additions and 122 deletions

View File

@ -44,3 +44,7 @@ MAIL_FROM_ADDRESS=null
MAIL_FROM_NAME="${APP_NAME}"
DOCKER_INSTALL_XDEBUG=false
GOOGLE_CLIENT_ID=
GOOGLE_CLIENT_SECRET=
GOOGLE_REDIRECT=

View File

@ -0,0 +1,41 @@
<?php
declare(strict_types=1);
namespace Toby\Http\Controllers;
use Illuminate\Contracts\Auth\Factory as AuthFactory;
use Illuminate\Database\Eloquent\ModelNotFoundException;
use Laravel\Socialite\SocialiteManager;
use Symfony\Component\HttpFoundation\RedirectResponse;
use Toby\Models\User;
class GoogleController extends Controller
{
public function redirect(SocialiteManager $socialiteManager): RedirectResponse
{
return $socialiteManager->driver("google")->redirect();
}
public function callback(AuthFactory $auth, SocialiteManager $socialiteManager): RedirectResponse
{
$socialUser = $socialiteManager->driver("google")->user();
try {
/** @var User $user */
$user = User::query()
->where("email", $socialUser->getEmail())
->firstOrFail();
} catch (ModelNotFoundException) {
return redirect()
->route("login")
->withErrors([
"oauth" => __("User does not exist."),
]);
}
$auth->guard()->login($user, true);
return redirect()->route("dashboard");
}
}

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

@ -8,10 +8,8 @@ use Illuminate\Auth\Middleware\Authenticate as Middleware;
class Authenticate extends Middleware
{
protected function redirectTo($request)
protected function redirectTo($request): string
{
if (!$request->expectsJson()) {
return route("login");
}
}
}

View File

@ -6,20 +6,17 @@ namespace Toby\Http\Middleware;
use Illuminate\Http\Request;
use Inertia\Middleware;
use Toby\Http\Resources\UserResource;
class HandleInertiaRequests extends Middleware
{
public function share(Request $request): array
{
$user = $request->user();
return array_merge(parent::share($request), [
"auth" => fn() => [
"user" => [
"name" => "Chelsea Hagon",
"email" => "chelseahagon@example.com",
"role" => "Human Resources Manager",
"imageUrl" =>
"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",
],
"user" => $user ? new UserResource($user) : null,
],
"flash" => fn() => [
"success" => $request->session()->get("success"),

View File

@ -8,7 +8,6 @@ use Closure;
use Illuminate\Http\Request;
use Illuminate\Support\Facades\Auth;
use Symfony\Component\HttpFoundation\Response;
use Toby\Providers\RouteServiceProvider;
class RedirectIfAuthenticated
{
@ -18,7 +17,7 @@ class RedirectIfAuthenticated
foreach ($guards as $guard) {
if (Auth::guard($guard)->check()) {
return redirect(RouteServiceProvider::HOME);
return redirect()->route("dashboard");
}
}

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

@ -8,6 +8,11 @@ use Illuminate\Database\Eloquent\Factories\HasFactory;
use Illuminate\Foundation\Auth\User as Authenticatable;
use Illuminate\Notifications\Notifiable;
/**
* @property int $id
* @property string $name
* @property string $email
*/
class User extends Authenticatable
{
use HasFactory;
@ -16,15 +21,9 @@ class User extends Authenticatable
protected $fillable = [
"name",
"email",
"password",
];
protected $hidden = [
"password",
"remember_token",
];
protected $casts = [
"email_verified_at" => "datetime",
];
}

View File

@ -12,8 +12,6 @@ use Illuminate\Support\Facades\Route;
class RouteServiceProvider extends ServiceProvider
{
public const HOME = "/home";
public function boot(): void
{
$this->configureRateLimiting();

View File

@ -11,6 +11,7 @@
"guzzlehttp/guzzle": "^7.0.1",
"inertiajs/inertia-laravel": "^0.5.1",
"laravel/framework": "^8.75",
"laravel/socialite": "^5.2",
"laravel/telescope": "^4.6",
"laravel/tinker": "^2.5"
},

258
composer.lock generated
View File

@ -4,7 +4,7 @@
"Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies",
"This file is @generated automatically"
],
"content-hash": "1f888e8a9e8764750ed43fbb8697721e",
"content-hash": "6c0c7586f9003a71d9299165b3d5030d",
"packages": [
{
"name": "asm89/stack-cors",
@ -366,16 +366,16 @@
},
{
"name": "dragonmantank/cron-expression",
"version": "v3.2.3",
"version": "v3.2.4",
"source": {
"type": "git",
"url": "https://github.com/dragonmantank/cron-expression.git",
"reference": "47c53bbb260d3c398fba9bfa9683dcf67add2579"
"reference": "9545dea2a1d92b60c8b3d06f02025c83e999bde0"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/dragonmantank/cron-expression/zipball/47c53bbb260d3c398fba9bfa9683dcf67add2579",
"reference": "47c53bbb260d3c398fba9bfa9683dcf67add2579",
"url": "https://api.github.com/repos/dragonmantank/cron-expression/zipball/9545dea2a1d92b60c8b3d06f02025c83e999bde0",
"reference": "9545dea2a1d92b60c8b3d06f02025c83e999bde0",
"shasum": ""
},
"require": {
@ -415,7 +415,7 @@
],
"support": {
"issues": "https://github.com/dragonmantank/cron-expression/issues",
"source": "https://github.com/dragonmantank/cron-expression/tree/v3.2.3"
"source": "https://github.com/dragonmantank/cron-expression/tree/v3.2.4"
},
"funding": [
{
@ -423,7 +423,7 @@
"type": "github"
}
],
"time": "2022-01-06T05:35:07+00:00"
"time": "2022-01-13T04:09:37+00:00"
},
{
"name": "egulias/email-validator",
@ -961,26 +961,26 @@
},
{
"name": "inertiajs/inertia-laravel",
"version": "v0.5.1",
"version": "v0.5.2",
"source": {
"type": "git",
"url": "https://github.com/inertiajs/inertia-laravel.git",
"reference": "1b2d57abcffc087f07d2d892f0197af559d23802"
"reference": "9c8c4201435aa0c11cb832242cf4c1b01bd8ef32"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/inertiajs/inertia-laravel/zipball/1b2d57abcffc087f07d2d892f0197af559d23802",
"reference": "1b2d57abcffc087f07d2d892f0197af559d23802",
"url": "https://api.github.com/repos/inertiajs/inertia-laravel/zipball/9c8c4201435aa0c11cb832242cf4c1b01bd8ef32",
"reference": "9c8c4201435aa0c11cb832242cf4c1b01bd8ef32",
"shasum": ""
},
"require": {
"ext-json": "*",
"laravel/framework": "^6.0|^7.0|^8.74",
"laravel/framework": "^6.0|^7.0|^8.74|^9.0",
"php": "^7.2|~8.0.0|~8.1.0"
},
"require-dev": {
"mockery/mockery": "^1.3.3",
"orchestra/testbench": "^4.0|^5.0|^6.4",
"orchestra/testbench": "^4.0|^5.0|^6.4|^7.0",
"phpunit/phpunit": "^8.0|^9.5.8",
"roave/security-advisories": "dev-master"
},
@ -1018,7 +1018,7 @@
],
"support": {
"issues": "https://github.com/inertiajs/inertia-laravel/issues",
"source": "https://github.com/inertiajs/inertia-laravel/tree/v0.5.1"
"source": "https://github.com/inertiajs/inertia-laravel/tree/v0.5.2"
},
"funding": [
{
@ -1026,20 +1026,20 @@
"type": "github"
}
],
"time": "2022-01-07T18:18:00+00:00"
"time": "2022-01-12T16:18:26+00:00"
},
{
"name": "laravel/framework",
"version": "v8.78.1",
"version": "v8.79.0",
"source": {
"type": "git",
"url": "https://github.com/laravel/framework.git",
"reference": "16359b5ebafba6579b397d7505b082a6d1bb2e31"
"reference": "8091f07558ff4a890435ff9d25fa9aca0189ad63"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/laravel/framework/zipball/16359b5ebafba6579b397d7505b082a6d1bb2e31",
"reference": "16359b5ebafba6579b397d7505b082a6d1bb2e31",
"url": "https://api.github.com/repos/laravel/framework/zipball/8091f07558ff4a890435ff9d25fa9aca0189ad63",
"reference": "8091f07558ff4a890435ff9d25fa9aca0189ad63",
"shasum": ""
},
"require": {
@ -1071,7 +1071,7 @@
"symfony/routing": "^5.4",
"symfony/var-dumper": "^5.4",
"tijsverkoyen/css-to-inline-styles": "^2.2.2",
"vlucas/phpdotenv": "^5.2",
"vlucas/phpdotenv": "^5.4.1",
"voku/portable-ascii": "^1.4.8"
},
"conflict": {
@ -1128,6 +1128,7 @@
"symfony/cache": "^5.4"
},
"suggest": {
"ably/ably-php": "Required to use the Ably broadcast driver (^1.0).",
"aws/aws-sdk-php": "Required to use the SQS queue driver, DynamoDb failed job storage and SES mail driver (^3.198.1).",
"brianium/paratest": "Required to run tests in parallel (^6.0).",
"doctrine/dbal": "Required to rename columns and drop SQLite columns (^2.13.3|^3.1.4).",
@ -1198,7 +1199,7 @@
"issues": "https://github.com/laravel/framework/issues",
"source": "https://github.com/laravel/framework"
},
"time": "2022-01-05T14:52:50+00:00"
"time": "2022-01-12T16:12:41+00:00"
},
{
"name": "laravel/serializable-closure",
@ -1260,28 +1261,97 @@
"time": "2021-11-30T15:53:04+00:00"
},
{
"name": "laravel/telescope",
"version": "v4.6.10",
"name": "laravel/socialite",
"version": "v5.3.0",
"source": {
"type": "git",
"url": "https://github.com/laravel/telescope.git",
"reference": "65f9fdfd978dd999b5c496cd5a8398abdf9056a2"
"url": "https://github.com/laravel/socialite.git",
"reference": "4e6f7e40de9a54ad641de5b8e29cdf1e73842e10"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/laravel/telescope/zipball/65f9fdfd978dd999b5c496cd5a8398abdf9056a2",
"reference": "65f9fdfd978dd999b5c496cd5a8398abdf9056a2",
"url": "https://api.github.com/repos/laravel/socialite/zipball/4e6f7e40de9a54ad641de5b8e29cdf1e73842e10",
"reference": "4e6f7e40de9a54ad641de5b8e29cdf1e73842e10",
"shasum": ""
},
"require": {
"ext-json": "*",
"laravel/framework": "^8.29",
"guzzlehttp/guzzle": "^6.0|^7.0",
"illuminate/http": "^6.0|^7.0|^8.0|^9.0",
"illuminate/support": "^6.0|^7.0|^8.0|^9.0",
"league/oauth1-client": "^1.0",
"php": "^7.2|^8.0"
},
"require-dev": {
"illuminate/contracts": "^6.0|^7.0|^8.0|^9.0",
"mockery/mockery": "^1.0",
"orchestra/testbench": "^4.0|^5.0|^6.0|^7.0",
"phpunit/phpunit": "^8.0|^9.3"
},
"type": "library",
"extra": {
"branch-alias": {
"dev-master": "5.x-dev"
},
"laravel": {
"providers": [
"Laravel\\Socialite\\SocialiteServiceProvider"
],
"aliases": {
"Socialite": "Laravel\\Socialite\\Facades\\Socialite"
}
}
},
"autoload": {
"psr-4": {
"Laravel\\Socialite\\": "src/"
}
},
"notification-url": "https://packagist.org/downloads/",
"license": [
"MIT"
],
"authors": [
{
"name": "Taylor Otwell",
"email": "taylor@laravel.com"
}
],
"description": "Laravel wrapper around OAuth 1 & OAuth 2 libraries.",
"homepage": "https://laravel.com",
"keywords": [
"laravel",
"oauth"
],
"support": {
"issues": "https://github.com/laravel/socialite/issues",
"source": "https://github.com/laravel/socialite"
},
"time": "2022-01-12T18:05:39+00:00"
},
{
"name": "laravel/telescope",
"version": "v4.7.0",
"source": {
"type": "git",
"url": "https://github.com/laravel/telescope.git",
"reference": "4c7f170e20c2303321fa857bc3acceb69a588563"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/laravel/telescope/zipball/4c7f170e20c2303321fa857bc3acceb69a588563",
"reference": "4c7f170e20c2303321fa857bc3acceb69a588563",
"shasum": ""
},
"require": {
"ext-json": "*",
"laravel/framework": "^8.29|^9.0",
"php": "^7.3|^8.0",
"symfony/var-dumper": "^5.0"
"symfony/var-dumper": "^5.0|^6.0"
},
"require-dev": {
"ext-gd": "*",
"orchestra/testbench": "^6.0"
"orchestra/testbench": "^6.0|^7.0"
},
"type": "library",
"extra": {
@ -1322,38 +1392,38 @@
],
"support": {
"issues": "https://github.com/laravel/telescope/issues",
"source": "https://github.com/laravel/telescope/tree/v4.6.10"
"source": "https://github.com/laravel/telescope/tree/v4.7.0"
},
"time": "2022-01-03T13:08:00+00:00"
"time": "2022-01-12T17:03:58+00:00"
},
{
"name": "laravel/tinker",
"version": "v2.6.3",
"version": "v2.7.0",
"source": {
"type": "git",
"url": "https://github.com/laravel/tinker.git",
"reference": "a9ddee4761ec8453c584e393b393caff189a3e42"
"reference": "5f2f9815b7631b9f586a3de7933c25f9327d4073"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/laravel/tinker/zipball/a9ddee4761ec8453c584e393b393caff189a3e42",
"reference": "a9ddee4761ec8453c584e393b393caff189a3e42",
"url": "https://api.github.com/repos/laravel/tinker/zipball/5f2f9815b7631b9f586a3de7933c25f9327d4073",
"reference": "5f2f9815b7631b9f586a3de7933c25f9327d4073",
"shasum": ""
},
"require": {
"illuminate/console": "^6.0|^7.0|^8.0",
"illuminate/contracts": "^6.0|^7.0|^8.0",
"illuminate/support": "^6.0|^7.0|^8.0",
"illuminate/console": "^6.0|^7.0|^8.0|^9.0",
"illuminate/contracts": "^6.0|^7.0|^8.0|^9.0",
"illuminate/support": "^6.0|^7.0|^8.0|^9.0",
"php": "^7.2.5|^8.0",
"psy/psysh": "^0.10.4",
"symfony/var-dumper": "^4.3.4|^5.0"
"psy/psysh": "^0.10.4|^0.11.1",
"symfony/var-dumper": "^4.3.4|^5.0|^6.0"
},
"require-dev": {
"mockery/mockery": "~1.3.3|^1.4.2",
"phpunit/phpunit": "^8.5.8|^9.3.3"
},
"suggest": {
"illuminate/database": "The Illuminate Database package (^6.0|^7.0|^8.0)."
"illuminate/database": "The Illuminate Database package (^6.0|^7.0|^8.0|^9.0)."
},
"type": "library",
"extra": {
@ -1390,9 +1460,9 @@
],
"support": {
"issues": "https://github.com/laravel/tinker/issues",
"source": "https://github.com/laravel/tinker/tree/v2.6.3"
"source": "https://github.com/laravel/tinker/tree/v2.7.0"
},
"time": "2021-12-07T16:41:42+00:00"
"time": "2022-01-10T08:52:49+00:00"
},
{
"name": "league/commonmark",
@ -1729,6 +1799,82 @@
],
"time": "2021-11-21T11:48:40+00:00"
},
{
"name": "league/oauth1-client",
"version": "v1.10.0",
"source": {
"type": "git",
"url": "https://github.com/thephpleague/oauth1-client.git",
"reference": "88dd16b0cff68eb9167bfc849707d2c40ad91ddc"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/thephpleague/oauth1-client/zipball/88dd16b0cff68eb9167bfc849707d2c40ad91ddc",
"reference": "88dd16b0cff68eb9167bfc849707d2c40ad91ddc",
"shasum": ""
},
"require": {
"ext-json": "*",
"ext-openssl": "*",
"guzzlehttp/guzzle": "^6.0|^7.0",
"guzzlehttp/psr7": "^1.7|^2.0",
"php": ">=7.1||>=8.0"
},
"require-dev": {
"ext-simplexml": "*",
"friendsofphp/php-cs-fixer": "^2.17",
"mockery/mockery": "^1.3.3",
"phpstan/phpstan": "^0.12.42",
"phpunit/phpunit": "^7.5||9.5"
},
"suggest": {
"ext-simplexml": "For decoding XML-based responses."
},
"type": "library",
"extra": {
"branch-alias": {
"dev-master": "1.0-dev",
"dev-develop": "2.0-dev"
}
},
"autoload": {
"psr-4": {
"League\\OAuth1\\Client\\": "src/"
}
},
"notification-url": "https://packagist.org/downloads/",
"license": [
"MIT"
],
"authors": [
{
"name": "Ben Corlett",
"email": "bencorlett@me.com",
"homepage": "http://www.webcomm.com.au",
"role": "Developer"
}
],
"description": "OAuth 1.0 Client Library",
"keywords": [
"Authentication",
"SSO",
"authorization",
"bitbucket",
"identity",
"idp",
"oauth",
"oauth1",
"single sign on",
"trello",
"tumblr",
"twitter"
],
"support": {
"issues": "https://github.com/thephpleague/oauth1-client/issues",
"source": "https://github.com/thephpleague/oauth1-client/tree/v1.10.0"
},
"time": "2021-08-15T23:05:49+00:00"
},
{
"name": "monolog/monolog",
"version": "2.3.5",
@ -2624,29 +2770,29 @@
},
{
"name": "psy/psysh",
"version": "v0.10.12",
"version": "v0.11.1",
"source": {
"type": "git",
"url": "https://github.com/bobthecow/psysh.git",
"reference": "a0d9981aa07ecfcbea28e4bfa868031cca121e7d"
"reference": "570292577277f06f590635381a7f761a6cf4f026"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/bobthecow/psysh/zipball/a0d9981aa07ecfcbea28e4bfa868031cca121e7d",
"reference": "a0d9981aa07ecfcbea28e4bfa868031cca121e7d",
"url": "https://api.github.com/repos/bobthecow/psysh/zipball/570292577277f06f590635381a7f761a6cf4f026",
"reference": "570292577277f06f590635381a7f761a6cf4f026",
"shasum": ""
},
"require": {
"ext-json": "*",
"ext-tokenizer": "*",
"nikic/php-parser": "~4.0|~3.0|~2.0|~1.3",
"php": "^8.0 || ^7.0 || ^5.5.9",
"symfony/console": "~5.0|~4.0|~3.0|^2.4.2|~2.3.10",
"symfony/var-dumper": "~5.0|~4.0|~3.0|~2.7"
"nikic/php-parser": "^4.0 || ^3.1",
"php": "^8.0 || ^7.0.8",
"symfony/console": "^6.0 || ^5.0 || ^4.0 || ^3.4",
"symfony/var-dumper": "^6.0 || ^5.0 || ^4.0 || ^3.4"
},
"require-dev": {
"bamarni/composer-bin-plugin": "^1.2",
"hoa/console": "3.17.*"
"hoa/console": "3.17.05.02"
},
"suggest": {
"ext-pcntl": "Enabling the PCNTL extension makes PsySH a lot happier :)",
@ -2661,7 +2807,7 @@
"type": "library",
"extra": {
"branch-alias": {
"dev-main": "0.10.x-dev"
"dev-main": "0.11.x-dev"
}
},
"autoload": {
@ -2693,9 +2839,9 @@
],
"support": {
"issues": "https://github.com/bobthecow/psysh/issues",
"source": "https://github.com/bobthecow/psysh/tree/v0.10.12"
"source": "https://github.com/bobthecow/psysh/tree/v0.11.1"
},
"time": "2021-11-30T14:05:36+00:00"
"time": "2022-01-03T13:58:38+00:00"
},
{
"name": "ralouphie/getallheaders",

View File

@ -2,4 +2,10 @@
declare(strict_types=1);
return [];
return [
"google" => [
"client_id" => env("GOOGLE_CLIENT_ID"),
"client_secret" => env("GOOGLE_CLIENT_SECRET"),
"redirect" => env("GOOGLE_REDIRECT"),
],
];

View File

@ -5,7 +5,6 @@ declare(strict_types=1);
namespace Database\Factories;
use Illuminate\Database\Eloquent\Factories\Factory;
use Illuminate\Support\Facades\Hash;
use Illuminate\Support\Str;
class UserFactory extends Factory
@ -15,8 +14,6 @@ class UserFactory extends Factory
return [
"name" => $this->faker->name(),
"email" => $this->faker->unique()->safeEmail(),
"email_verified_at" => now(),
"password" => Hash::make("secret123"),
"remember_token" => Str::random(10),
];
}

View File

@ -13,8 +13,6 @@ return new class() extends Migration {
$table->id();
$table->string("name");
$table->string("email")->unique();
$table->timestamp("email_verified_at")->nullable();
$table->string("password");
$table->rememberToken();
$table->timestamps();
});

44
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",
@ -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",
@ -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/logo-white.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 6.7 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 6.7 KiB

After

Width:  |  Height:  |  Size: 6.9 KiB

View File

@ -62,7 +62,7 @@ If xDebug is installed, set environment variable **XDEBUG_MODE=off** to improve
dcr php php vendor/bin/ecs check --fix
dcr php composer ecsf
dcr node npm run lint
dcr node rpm run lintf
dcr node npm run lintf
### xDebug

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>
@ -255,7 +255,6 @@ import {
} from '@heroicons/vue/outline';
import {computed} from 'vue';
import {usePage} from '@inertiajs/inertia-vue3';
export default {
name: 'Dashboard',
setup() {
@ -362,7 +361,6 @@ export default {
'Tenetur libero voluptatem rerum occaecati qui est molestiae exercitationem. Voluptate quisquam iure assumenda consequatur ex et recusandae. Alias consectetur voluptatibus. Accusamus a ab dicta et. Consequatur quis dignissimos voluptatem nisi.',
},
];
return {
user,
stats,

View File

@ -0,0 +1,85 @@
<template>
<InertiaHead title="Zaloguj się" />
<transition
enter-active-class="transform ease-out duration-300 transition"
enter-from-class="translate-y-2 opacity-0 sm:translate-y-0 sm:translate-x-2"
enter-to-class="translate-y-0 opacity-100 sm:translate-x-0"
leave-active-class="transition ease-in duration-100"
leave-from-class="opacity-100"
leave-to-class="opacity-0"
>
<div
v-if="errors.oauth"
class="absolute inset-x-2 top-2 sm:mx-auto sm:w-full sm:max-w-md bg-red-500 shadow-lg rounded-lg pointer-events-auto ring-1 ring-black ring-opacity-5 overflow-hidden"
>
<div class="p-4">
<div class="flex items-center">
<div class="w-0 flex-1 flex justify-between">
<ExclamationIcon class="h-5 w-5 text-white" />
<p class="w-0 flex-1 text-sm font-medium text-white">
{{ errors.oauth }}
</p>
</div>
<div class="ml-4 flex-shrink-0 flex">
<button
class="bg-red-500 rounded-md inline-flex text-red-100 hover:text-red-400 focus:outline-none focus:ring-2 focus:ring-offset-2 focus:ring-red-600"
@click="delete errors.oauth"
>
<span class="sr-only">Close</span>
<XIcon
class="h-5 w-5"
aria-hidden="true"
/>
</button>
</div>
</div>
</div>
</div>
</transition>
<div
class="sm:mx-auto sm:w-full sm:max-w-md text-white space-y-4 flex flex-col items-center rounded-lg px-4 py-8"
>
<img
class="mx-auto h-50 w-auto"
src="img/logo.png"
alt="Blumilk"
>
<a
href="/login/google/start"
class="inline-flex justify-center py-2 px-6 rounded-md shadow-sm bg-blumilk-500 text-md font-medium text-white hover:bg-blumilk-700"
>
Zaloguj się za pomocą Google
<svg
class="w-5 h-5 ml-2"
fill="currentColor"
viewBox="0 0 24 24"
>
<path
d="M6 12C6 15.3137 8.68629 18 12 18C14.6124 18 16.8349 16.3304 17.6586 14H12V10H21.8047V14H21.8C20.8734 18.5645 16.8379 22 12 22C6.47715 22 2 17.5228 2 12C2 6.47715 6.47715 2 12 2C15.445 2 18.4831 3.742 20.2815 6.39318L17.0039 8.68815C15.9296 7.06812 14.0895 6 12 6C8.68629 6 6 8.68629 6 12Z"
fill="currentColor"
/>
</svg>
</a>
</div>
</template>
<script>
import GuestLayout from '@/Shared/Layout/GuestLayout';
import {XIcon} from '@heroicons/vue/solid';
import {ExclamationIcon} from '@heroicons/vue/solid';
export default {
name: 'Login',
components: {
XIcon,
ExclamationIcon,
},
layout: GuestLayout,
props: {
errors: {
type: Object,
default: () => ({oauth: null}),
}
},
};
</script>

View File

@ -0,0 +1,19 @@
<template>
<div class="min-h-full">
<MainMenu />
<main class="-mt-24 pb-8">
<div class="max-w-3xl mx-auto px-4 sm:px-6 lg:max-w-7xl lg:px-8">
<slot />
</div>
</main>
</div>
</template>
<script>
import MainMenu from '@/Shared/MainMenu';
export default {
name: 'AppLayout',
components: {MainMenu},
};
</script>

View File

@ -0,0 +1,12 @@
<template>
<div class="min-h-screen flex flex-col justify-center py-12 sm:px-6 lg:px-8 bg-blumilk-25">
<slot />
</div>
</template>
<script>
export default {
name: 'GuestLayout',
};
</script>

View File

@ -11,7 +11,7 @@
<InertiaLink href="/">
<img
class="h-8 w-auto"
src="/img/logo.png"
src="/img/logo-white.png"
alt="Workflow"
>
</InertiaLink>
@ -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

@ -1,13 +1,13 @@
import {createApp, h} from 'vue';
import {createInertiaApp, Head, Link} from '@inertiajs/inertia-vue3';
import {InertiaProgress} from '@inertiajs/progress';
import Layout from '@/Shared/Layout';
import AppLayout from '@/Shared/Layout/AppLayout';
createInertiaApp({
resolve: name => {
const page = require(`./Pages/${name}`).default;
page.layout = page.layout || Layout;
page.layout = page.layout || AppLayout;
return page;
},

3
resources/lang/pl.json Normal file
View File

@ -0,0 +1,3 @@
{
"User does not exist.": "Użytkownik nie istnieje."
}

View File

@ -3,5 +3,18 @@
declare(strict_types=1);
use Illuminate\Support\Facades\Route;
use Toby\Http\Controllers\GoogleController;
use Toby\Http\Controllers\LogoutController;
Route::get("/", fn() => inertia("Dashboard"));
Route::middleware("auth")->group(function (): void {
Route::get("/", fn() => inertia("Dashboard"))->name("dashboard");
Route::post("/logout", LogoutController::class);
});
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

@ -11,6 +11,7 @@ module.exports = {
},
colors: {
'blumilk': {
'25': '#F4F8FD',
'50': '#D5DFEE',
'100': '#C7D4E9',
'200': '#AABDDD',

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"));