From 38c0c70ded79b89bae3ad6e6057420d030c8c751 Mon Sep 17 00:00:00 2001 From: Javier Feliz Date: Sun, 27 Jul 2025 22:18:35 -0400 Subject: [PATCH] Bunch of progress --- .gitignore | 3 +- app/Http/Controllers/OIDCController.php | 12 ++- app/Livewire/Forms/UserProfile.php | 93 +++++++++++++++++++ app/Livewire/Settings/Profile.php | 1 - app/Models/User.php | 7 ++ config/filesystems.php | 11 ++- .../2025_07_27_215631_add_avatar_to_users.php | 30 ++++++ .../components/layouts/app/header.blade.php | 6 +- .../views/components/layouts/auth.blade.php | 4 +- .../components/layouts/auth/split.blade.php | 82 +++++++++------- resources/views/dashboard.blade.php | 3 + .../livewire/forms/user-profile.blade.php | 90 ++++++++++++++++++ routes/web.php | 8 ++ vite.config.js | 12 ++- 14 files changed, 316 insertions(+), 46 deletions(-) create mode 100644 app/Livewire/Forms/UserProfile.php create mode 100644 database/migrations/2025_07_27_215631_add_avatar_to_users.php create mode 100644 resources/views/livewire/forms/user-profile.blade.php diff --git a/.gitignore b/.gitignore index 7d71d59..3601ded 100644 --- a/.gitignore +++ b/.gitignore @@ -21,4 +21,5 @@ yarn-error.log /.nova /.vscode /.zed -/storage/oauth/* \ No newline at end of file +/storage/oauth/* +/storage/avatars/* \ No newline at end of file diff --git a/app/Http/Controllers/OIDCController.php b/app/Http/Controllers/OIDCController.php index 9e73e01..8c35a36 100644 --- a/app/Http/Controllers/OIDCController.php +++ b/app/Http/Controllers/OIDCController.php @@ -172,7 +172,8 @@ class OIDCController extends Controller 'sub' => (string) $user->id, 'email' => $user->email, 'name' => $user->name, - 'preferred_username' => str($user->name)->slug()->toString(), + 'preferred_username' => $user->preferred_username, + 'picture' => $user->avatar ? $user->avatarUrl() : null ]); } @@ -206,7 +207,14 @@ class OIDCController extends Controller 'scopes_supported' => ["openid", "profile", "email"], 'response_types_supported' => ["code"], "jwks_uri" => route('auth.keys'), - "id_token_signing_alg_values_supported" => ["RS256"] + "id_token_signing_alg_values_supported" => ["RS256"], + 'claims_supported' => [ + 'sub', + 'email', + 'name', + 'preferred_username', + 'picture' + ] ]); } } diff --git a/app/Livewire/Forms/UserProfile.php b/app/Livewire/Forms/UserProfile.php new file mode 100644 index 0000000..26bd403 --- /dev/null +++ b/app/Livewire/Forms/UserProfile.php @@ -0,0 +1,93 @@ +name = Auth::user()->name; + $this->email = Auth::user()->email; + $this->preferred_username = Auth::user()->preferred_username; + $this->avatar = Auth::user()->avatar; + } + + /** + * Update the profile information for the currently authenticated user. + */ + public function updateProfileInformation(): void + { + $user = Auth::user(); + + $validated = $this->validate([ + 'name' => 'required|string|max:255', + 'email' => [ + 'required', + 'string', + 'lowercase', + 'email', + 'max:255', + Rule::unique(User::class)->ignore($user->id), + ], + 'preferred_username' => 'string|max:255' + ]); + + $user->fill($validated); + + if (!empty($this->avatarUpload)) { + if (!empty($user->avatar)) { + Storage::disk('avatars')->delete($user->avatar); + } + $user->avatar = $this->avatarUpload->store(options: 'avatars'); + } + + $user->save(); + + $this->dispatch('profile-updated', name: $user->name); + } + + /** + * Send an email verification notification to the current user. + */ + public function resendVerificationNotification(): void + { + $user = Auth::user(); + + if ($user->hasVerifiedEmail()) { + $this->redirectIntended(default: route('dashboard', absolute: false)); + + return; + } + + $user->sendEmailVerificationNotification(); + + Session::flash('status', 'verification-link-sent'); + } + + public function render() + { + return view('livewire.forms.user-profile'); + } +} diff --git a/app/Livewire/Settings/Profile.php b/app/Livewire/Settings/Profile.php index c823a69..b0a65ee 100644 --- a/app/Livewire/Settings/Profile.php +++ b/app/Livewire/Settings/Profile.php @@ -11,7 +11,6 @@ use Livewire\Component; class Profile extends Component { public string $name = ''; - public string $email = ''; /** diff --git a/app/Models/User.php b/app/Models/User.php index cc13521..71cafdf 100644 --- a/app/Models/User.php +++ b/app/Models/User.php @@ -23,6 +23,8 @@ class User extends Authenticatable 'name', 'email', 'password', + 'avatar', + 'preferred_username' ]; /** @@ -64,4 +66,9 @@ class User extends Authenticatable { return $this->hasMany(AuthenticationToken::class); } + + public function avatarUrl() + { + return route('user.avatar', ['path' => $this->avatar]); + } } diff --git a/config/filesystems.php b/config/filesystems.php index 3d671bd..42838a2 100644 --- a/config/filesystems.php +++ b/config/filesystems.php @@ -41,12 +41,21 @@ return [ 'public' => [ 'driver' => 'local', 'root' => storage_path('app/public'), - 'url' => env('APP_URL').'/storage', + 'url' => env('APP_URL') . '/storage', 'visibility' => 'public', 'throw' => false, 'report' => false, ], + 'avatars' => [ + 'driver' => 'local', + 'root' => storage_path('avatars'), + 'url' => env('APP_URL') . '/storage', + 'visibility' => 'public', + 'throw' => true, + 'report' => true, + ], + 's3' => [ 'driver' => 's3', 'key' => env('AWS_ACCESS_KEY_ID'), diff --git a/database/migrations/2025_07_27_215631_add_avatar_to_users.php b/database/migrations/2025_07_27_215631_add_avatar_to_users.php new file mode 100644 index 0000000..8a81a03 --- /dev/null +++ b/database/migrations/2025_07_27_215631_add_avatar_to_users.php @@ -0,0 +1,30 @@ +string('avatar')->nullable()->after('email'); + $table->string('preferred_username')->nullable()->after('avatar'); + }); + } + + /** + * Reverse the migrations. + */ + public function down(): void + { + Schema::table('users', function (Blueprint $table) { + $table->dropColumn('avatar'); + $table->dropColumn('preferred_username'); + }); + } +}; diff --git a/resources/views/components/layouts/app/header.blade.php b/resources/views/components/layouts/app/header.blade.php index 4150d82..015d0d0 100644 --- a/resources/views/components/layouts/app/header.blade.php +++ b/resources/views/components/layouts/app/header.blade.php @@ -57,8 +57,10 @@ - {{ __('Settings') }} - + + {{ __('Edit your profile') }} + + diff --git a/resources/views/components/layouts/auth.blade.php b/resources/views/components/layouts/auth.blade.php index 56d6cb6..2e986ec 100644 --- a/resources/views/components/layouts/auth.blade.php +++ b/resources/views/components/layouts/auth.blade.php @@ -1,3 +1,3 @@ - + {{ $slot }} - + \ No newline at end of file diff --git a/resources/views/components/layouts/auth/split.blade.php b/resources/views/components/layouts/auth/split.blade.php index 4e9788b..d384e2c 100644 --- a/resources/views/components/layouts/auth/split.blade.php +++ b/resources/views/components/layouts/auth/split.blade.php @@ -1,43 +1,53 @@ - - @include('partials.head') - - -
- -
-
- - - - + +
+ + @php + [$message, $author] = str(Illuminate\Foundation\Inspiring::quotes()->random())->explode('-'); + @endphp + +
+
+ “{{ trim($message) }}” +
+ {{ trim($author) }} +
+
- @fluxScripts - - + +
+ @fluxScripts + + + \ No newline at end of file diff --git a/resources/views/dashboard.blade.php b/resources/views/dashboard.blade.php index b514e53..3663558 100644 --- a/resources/views/dashboard.blade.php +++ b/resources/views/dashboard.blade.php @@ -1,5 +1,8 @@
+
+ +
\ No newline at end of file diff --git a/resources/views/livewire/forms/user-profile.blade.php b/resources/views/livewire/forms/user-profile.blade.php new file mode 100644 index 0000000..a9cb4c9 --- /dev/null +++ b/resources/views/livewire/forms/user-profile.blade.php @@ -0,0 +1,90 @@ +
+
+
+
+ @if (!empty($avatarUpload)) + + @elseif(!empty($avatar)) + + @else +
+
{{auth()->user()->initials()}}
+
+ @endif +
+
+ {{auth()->user()->name}} + {{auth()->user()->preferred_username}} (preferred username) + + {{auth()->user()->email}} +
+
+
+
+
+ + @if (!empty($avatarUpload)) + + @elseif(!empty($avatar)) + + @else +
+
{{auth()->user()->initials()}}
+
+ @endif +
+
+ + + +
+
+ +
+
+ {{ __('Save') }} + Cancel +
+ + + {{ __('Saved.') }} + +
+
+
+ Edit Profile information + +
+
+
+ Change Password +
+ + + + +
+
+ {{ __('Save') }} + Cancel +
+ + + {{ __('Saved.') }} + +
+ +
+
\ No newline at end of file diff --git a/routes/web.php b/routes/web.php index 03d7e89..e6c5587 100644 --- a/routes/web.php +++ b/routes/web.php @@ -5,8 +5,10 @@ use App\Livewire\ConsentScreen; use App\Livewire\Settings\Appearance; use App\Livewire\Settings\Password; use App\Livewire\Settings\Profile; +use App\Models\User; use Illuminate\Foundation\Http\Middleware\VerifyCsrfToken; use Illuminate\Support\Facades\Route; +use Illuminate\Support\Facades\Storage; Route::get('/', function () { return view('welcome'); @@ -24,6 +26,12 @@ Route::middleware(['auth'])->group(function () { Route::get('settings/appearance', Appearance::class)->name('settings.appearance'); }); +Route::get('avatars/{path}', function (string $path) { + $path = Storage::disk('avatars')->path($path); + + return response()->file($path); +})->name('user.avatar'); + // OIDC Endpoints Route::prefix('application/o')->group(function () { Route::get('authorize', [OIDCController::class, 'authorize'])->middleware('auth')->name('auth.authorize'); diff --git a/vite.config.js b/vite.config.js index f5f5138..f3dadd4 100644 --- a/vite.config.js +++ b/vite.config.js @@ -3,6 +3,8 @@ import { } from 'vite'; import laravel from 'laravel-vite-plugin'; import tailwindcss from "@tailwindcss/vite"; +import fs from 'fs' +import path from 'path' export default defineConfig({ plugins: [ @@ -14,6 +16,14 @@ export default defineConfig({ ], server: { cors: true, - host: "homelab-sso.test" + host: "homelab-sso.test", + // https: { + // key: fs.readFileSync(path.resolve( + // process.env.HOME, '.valet/Certificates/homelab-sso.test.key' + // )), + // cert: fs.readFileSync(path.resolve( + // process.env.HOME, '.valet/Certificates/homelab-sso.test.crt' + // )), + // }, }, }); \ No newline at end of file