first commit
This commit is contained in:
commit
405df0a122
3083 changed files with 69203 additions and 0 deletions
40
app/Actions/Fortify/CreateNewUser.php
Normal file
40
app/Actions/Fortify/CreateNewUser.php
Normal file
|
|
@ -0,0 +1,40 @@
|
|||
<?php
|
||||
|
||||
namespace App\Actions\Fortify;
|
||||
|
||||
use App\Models\User;
|
||||
use Illuminate\Support\Facades\Hash;
|
||||
use Illuminate\Support\Facades\Validator;
|
||||
use Illuminate\Validation\Rule;
|
||||
use Laravel\Fortify\Contracts\CreatesNewUsers;
|
||||
|
||||
class CreateNewUser implements CreatesNewUsers
|
||||
{
|
||||
use PasswordValidationRules;
|
||||
|
||||
/**
|
||||
* Validate and create a newly registered user.
|
||||
*
|
||||
* @param array<string, string> $input
|
||||
*/
|
||||
public function create(array $input): User
|
||||
{
|
||||
Validator::make($input, [
|
||||
'name' => ['required', 'string', 'max:255'],
|
||||
'email' => [
|
||||
'required',
|
||||
'string',
|
||||
'email',
|
||||
'max:255',
|
||||
Rule::unique(User::class),
|
||||
],
|
||||
'password' => $this->passwordRules(),
|
||||
])->validate();
|
||||
|
||||
return User::create([
|
||||
'name' => $input['name'],
|
||||
'email' => $input['email'],
|
||||
'password' => Hash::make($input['password']),
|
||||
]);
|
||||
}
|
||||
}
|
||||
18
app/Actions/Fortify/PasswordValidationRules.php
Normal file
18
app/Actions/Fortify/PasswordValidationRules.php
Normal file
|
|
@ -0,0 +1,18 @@
|
|||
<?php
|
||||
|
||||
namespace App\Actions\Fortify;
|
||||
|
||||
use Illuminate\Validation\Rules\Password;
|
||||
|
||||
trait PasswordValidationRules
|
||||
{
|
||||
/**
|
||||
* Get the validation rules used to validate passwords.
|
||||
*
|
||||
* @return array<int, \Illuminate\Contracts\Validation\Rule|array<mixed>|string>
|
||||
*/
|
||||
protected function passwordRules(): array
|
||||
{
|
||||
return ['required', 'string', Password::default(), 'confirmed'];
|
||||
}
|
||||
}
|
||||
29
app/Actions/Fortify/ResetUserPassword.php
Normal file
29
app/Actions/Fortify/ResetUserPassword.php
Normal file
|
|
@ -0,0 +1,29 @@
|
|||
<?php
|
||||
|
||||
namespace App\Actions\Fortify;
|
||||
|
||||
use App\Models\User;
|
||||
use Illuminate\Support\Facades\Hash;
|
||||
use Illuminate\Support\Facades\Validator;
|
||||
use Laravel\Fortify\Contracts\ResetsUserPasswords;
|
||||
|
||||
class ResetUserPassword implements ResetsUserPasswords
|
||||
{
|
||||
use PasswordValidationRules;
|
||||
|
||||
/**
|
||||
* Validate and reset the user's forgotten password.
|
||||
*
|
||||
* @param array<string, string> $input
|
||||
*/
|
||||
public function reset(User $user, array $input): void
|
||||
{
|
||||
Validator::make($input, [
|
||||
'password' => $this->passwordRules(),
|
||||
])->validate();
|
||||
|
||||
$user->forceFill([
|
||||
'password' => Hash::make($input['password']),
|
||||
])->save();
|
||||
}
|
||||
}
|
||||
32
app/Actions/Fortify/UpdateUserPassword.php
Normal file
32
app/Actions/Fortify/UpdateUserPassword.php
Normal file
|
|
@ -0,0 +1,32 @@
|
|||
<?php
|
||||
|
||||
namespace App\Actions\Fortify;
|
||||
|
||||
use App\Models\User;
|
||||
use Illuminate\Support\Facades\Hash;
|
||||
use Illuminate\Support\Facades\Validator;
|
||||
use Laravel\Fortify\Contracts\UpdatesUserPasswords;
|
||||
|
||||
class UpdateUserPassword implements UpdatesUserPasswords
|
||||
{
|
||||
use PasswordValidationRules;
|
||||
|
||||
/**
|
||||
* Validate and update the user's password.
|
||||
*
|
||||
* @param array<string, string> $input
|
||||
*/
|
||||
public function update(User $user, array $input): void
|
||||
{
|
||||
Validator::make($input, [
|
||||
'current_password' => ['required', 'string', 'current_password:web'],
|
||||
'password' => $this->passwordRules(),
|
||||
], [
|
||||
'current_password.current_password' => __('The provided password does not match your current password.'),
|
||||
])->validateWithBag('updatePassword');
|
||||
|
||||
$user->forceFill([
|
||||
'password' => Hash::make($input['password']),
|
||||
])->save();
|
||||
}
|
||||
}
|
||||
58
app/Actions/Fortify/UpdateUserProfileInformation.php
Normal file
58
app/Actions/Fortify/UpdateUserProfileInformation.php
Normal file
|
|
@ -0,0 +1,58 @@
|
|||
<?php
|
||||
|
||||
namespace App\Actions\Fortify;
|
||||
|
||||
use App\Models\User;
|
||||
use Illuminate\Contracts\Auth\MustVerifyEmail;
|
||||
use Illuminate\Support\Facades\Validator;
|
||||
use Illuminate\Validation\Rule;
|
||||
use Laravel\Fortify\Contracts\UpdatesUserProfileInformation;
|
||||
|
||||
class UpdateUserProfileInformation implements UpdatesUserProfileInformation
|
||||
{
|
||||
/**
|
||||
* Validate and update the given user's profile information.
|
||||
*
|
||||
* @param array<string, string> $input
|
||||
*/
|
||||
public function update(User $user, array $input): void
|
||||
{
|
||||
Validator::make($input, [
|
||||
'name' => ['required', 'string', 'max:255'],
|
||||
|
||||
'email' => [
|
||||
'required',
|
||||
'string',
|
||||
'email',
|
||||
'max:255',
|
||||
Rule::unique('users')->ignore($user->id),
|
||||
],
|
||||
])->validateWithBag('updateProfileInformation');
|
||||
|
||||
if ($input['email'] !== $user->email &&
|
||||
$user instanceof MustVerifyEmail) {
|
||||
$this->updateVerifiedUser($user, $input);
|
||||
} else {
|
||||
$user->forceFill([
|
||||
'name' => $input['name'],
|
||||
'email' => $input['email'],
|
||||
])->save();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Update the given verified user's profile information.
|
||||
*
|
||||
* @param array<string, string> $input
|
||||
*/
|
||||
protected function updateVerifiedUser(User $user, array $input): void
|
||||
{
|
||||
$user->forceFill([
|
||||
'name' => $input['name'],
|
||||
'email' => $input['email'],
|
||||
'email_verified_at' => null,
|
||||
])->save();
|
||||
|
||||
$user->sendEmailVerificationNotification();
|
||||
}
|
||||
}
|
||||
72
app/Console/Commands/GenerateDomainFavicons.php
Normal file
72
app/Console/Commands/GenerateDomainFavicons.php
Normal file
|
|
@ -0,0 +1,72 @@
|
|||
<?php
|
||||
|
||||
namespace App\Console\Commands;
|
||||
|
||||
use Illuminate\Console\Command;
|
||||
use Illuminate\Support\Facades\File;
|
||||
|
||||
class GenerateDomainFavicons extends Command
|
||||
{
|
||||
/**
|
||||
* The name and signature of the console command.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
protected $signature = 'domains:generate-favicons';
|
||||
|
||||
/**
|
||||
* The console command description.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
protected $description = 'Generiere Favicons für alle konfigurierten Domains';
|
||||
|
||||
/**
|
||||
* Execute the console command.
|
||||
*/
|
||||
public function handle()
|
||||
{
|
||||
$domains = config('domains');
|
||||
$faviconDir = public_path('img/favicons');
|
||||
|
||||
// Erstelle das Favicon-Verzeichnis, wenn es nicht existiert
|
||||
if (!File::exists($faviconDir)) {
|
||||
File::makeDirectory($faviconDir, 0755, true);
|
||||
$this->info("Verzeichnis {$faviconDir} erstellt.");
|
||||
}
|
||||
|
||||
// Erstelle eine Liste der Themes, für die wir Favicons erstellen müssen
|
||||
$themes = [];
|
||||
|
||||
foreach ($domains as $domain => $config) {
|
||||
if (is_array($config) && isset($config['theme'])) {
|
||||
$themes[$config['theme']] = [
|
||||
'domain' => $domain,
|
||||
'name' => $config['name'] ?? 'Website',
|
||||
'color' => $config['color_scheme']['primary'] ?? '#000000',
|
||||
];
|
||||
}
|
||||
}
|
||||
|
||||
// Erstelle die Favicons
|
||||
foreach ($themes as $theme => $data) {
|
||||
$faviconPath = "{$faviconDir}/{$theme}-favicon.ico";
|
||||
|
||||
// Wenn die Datei bereits existiert, frage, ob sie überschrieben werden soll
|
||||
if (File::exists($faviconPath) && !$this->confirm("Favicon für '{$theme}' existiert bereits. Überschreiben?")) {
|
||||
$this->info("Favicon für '{$theme}' übersprungen.");
|
||||
continue;
|
||||
}
|
||||
|
||||
// Hier könntest du mit einer externen Bibliothek einen Favicon erstellen
|
||||
// Da dies aber komplex sein kann, erstellen wir erstmal nur Platzhalter-Dateien
|
||||
File::put($faviconPath, '');
|
||||
|
||||
$this->info("Platzhalter-Favicon für '{$theme}' erstellt: {$faviconPath}");
|
||||
$this->comment("Ersetze diese Datei mit einem echten Favicon für {$data['domain']} ({$data['name']})");
|
||||
}
|
||||
|
||||
$this->info('Favicons wurden erfolgreich erstellt!');
|
||||
$this->line('Denke daran, die Platzhalter-Dateien mit echten Favicons zu ersetzen.');
|
||||
}
|
||||
}
|
||||
138
app/Console/Commands/MigratePresseData.php
Normal file
138
app/Console/Commands/MigratePresseData.php
Normal file
|
|
@ -0,0 +1,138 @@
|
|||
<?php
|
||||
|
||||
namespace App\Console\Commands;
|
||||
|
||||
use Illuminate\Console\Command;
|
||||
use Illuminate\Support\Facades\DB;
|
||||
use Illuminate\Support\Facades\Hash;
|
||||
use Illuminate\Support\Str;
|
||||
|
||||
// Import der Models für die NEUE Struktur
|
||||
use App\Models\User as NewUser;
|
||||
use App\Models\Article as NewArticle;
|
||||
use App\Models\Brand;
|
||||
|
||||
// Import der Models für die ALTE Struktur
|
||||
use App\Models\Legacy\Presseecho\User as LegacyUser;
|
||||
use App\Models\Legacy\Presseecho\Article as LegacyArticle;
|
||||
|
||||
class MigratePresseechoData extends Command
|
||||
{
|
||||
/**
|
||||
* The name and signature of the console command.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
protected $signature = 'migrate:presseecho';
|
||||
|
||||
/**
|
||||
* The console command description.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
protected $description = 'Migrates all data from the old presseecho.de database to the new central database';
|
||||
|
||||
/**
|
||||
* Execute the console command.
|
||||
*/
|
||||
public function handle()
|
||||
{
|
||||
$this->info('Starting migration for presseecho.de...');
|
||||
|
||||
// Holen der Brand-ID für Presseecho aus der neuen DB
|
||||
$brand = Brand::where('domain', 'presseecho.de')->first();
|
||||
if (!$brand) {
|
||||
$this->error('Brand "presseecho.de" not found in the new database. Please seed brands first.');
|
||||
return 1;
|
||||
}
|
||||
|
||||
// Deaktivieren von Foreign Key Checks für reibungsloses Einfügen
|
||||
DB::connection('mysql')->statement('SET FOREIGN_KEY_CHECKS=0;');
|
||||
|
||||
// Starten der einzelnen Migrationsschritte
|
||||
$this->migrateUsers();
|
||||
$this->migrateArticles($brand->id);
|
||||
// $this->migrateCategories($brand->id); // Weitere Schritte hier hinzufügen
|
||||
|
||||
// Reaktivieren der Foreign Key Checks
|
||||
DB::connection('mysql')->statement('SET FOREIGN_KEY_CHECKS=1;');
|
||||
|
||||
$this->info('Migration for presseecho.de completed successfully!');
|
||||
return 0;
|
||||
}
|
||||
|
||||
private function migrateUsers()
|
||||
{
|
||||
$this->line('Migrating users...');
|
||||
$progressBar = $this->output->createProgressBar(LegacyUser::count());
|
||||
$progressBar->start();
|
||||
|
||||
// Chunking verwenden, um den Speicher bei vielen Nutzern zu schonen
|
||||
LegacyUser::chunk(200, function ($legacyUsers) use ($progressBar) {
|
||||
foreach ($legacyUsers as $legacyUser) {
|
||||
// Prüfen, ob der Nutzer (anhand der E-Mail) bereits in der neuen DB existiert
|
||||
$existingUser = NewUser::where('email', $legacyUser->email)->first();
|
||||
|
||||
if (!$existingUser) {
|
||||
// Nutzer existiert noch nicht -> neu anlegen
|
||||
NewUser::create([
|
||||
'name' => $legacyUser->name,
|
||||
'email' => $legacyUser->email,
|
||||
'password' => $legacyUser->password, // Annahme: Passwörter sind bereits gehasht. Wenn nicht: Hash::make(Str::random(16)) und Passwort-Reset erzwingen
|
||||
'email_verified_at' => $legacyUser->created_at, // oder ein anderer Zeitstempel
|
||||
'created_at' => $legacyUser->created_at,
|
||||
'updated_at' => $legacyUser->updated_at,
|
||||
// ... weitere Felder mappen
|
||||
]);
|
||||
}
|
||||
$progressBar->advance();
|
||||
}
|
||||
});
|
||||
|
||||
$progressBar->finish();
|
||||
$this->newLine(2);
|
||||
}
|
||||
|
||||
private function migrateArticles(int $brandId)
|
||||
{
|
||||
$this->line('Migrating articles...');
|
||||
$progressBar = $this->output->createProgressBar(LegacyArticle::count());
|
||||
$progressBar->start();
|
||||
|
||||
LegacyArticle::chunk(200, function ($legacyArticles) use ($brandId, $progressBar) {
|
||||
foreach ($legacyArticles as $legacyArticle) {
|
||||
// Den zugehörigen Nutzer in der NEUEN Datenbank finden
|
||||
$author = NewUser::where('email', $legacyArticle->author_email)->first(); // Annahme: Artikel hat eine Autoren-E-Mail
|
||||
|
||||
if (!$author) {
|
||||
$this->warn("Skipping article ID {$legacyArticle->id}, author with email {$legacyArticle->author_email} not found.");
|
||||
continue; // Artikel überspringen, wenn kein Autor gefunden wurde
|
||||
}
|
||||
|
||||
// Neuen Artikel in der zentralen DB erstellen
|
||||
$newArticle = NewArticle::create([
|
||||
'user_id' => $author->id,
|
||||
'title' => $legacyArticle->title,
|
||||
'slug' => $legacyArticle->slug, // oder neu generieren: Str::slug($legacyArticle->title)
|
||||
'content' => $legacyArticle->body, // Spaltennamen anpassen
|
||||
'created_at' => $legacyArticle->created_at,
|
||||
'updated_at' => $legacyArticle->updated_at,
|
||||
// ... weitere Felder mappen
|
||||
]);
|
||||
|
||||
// Den Artikel mit der Plattform "Presseecho" verknüpfen
|
||||
// Dies fügt den Eintrag in die Pivot-Tabelle `article_platform` ein
|
||||
$newArticle->platforms()->attach($brandId, [
|
||||
'status' => 'published', // oder den alten Status übernehmen
|
||||
'published_at' => $legacyArticle->published_date,
|
||||
// eventuell weitere Pivot-Daten
|
||||
]);
|
||||
|
||||
$progressBar->advance();
|
||||
}
|
||||
});
|
||||
|
||||
$progressBar->finish();
|
||||
$this->newLine(2);
|
||||
}
|
||||
}
|
||||
36
app/Console/Kernel.php
Normal file
36
app/Console/Kernel.php
Normal file
|
|
@ -0,0 +1,36 @@
|
|||
<?php
|
||||
|
||||
namespace App\Console;
|
||||
|
||||
use Illuminate\Console\Scheduling\Schedule;
|
||||
use Illuminate\Foundation\Console\Kernel as ConsoleKernel;
|
||||
|
||||
class Kernel extends ConsoleKernel
|
||||
{
|
||||
/**
|
||||
* The Artisan commands provided by your application.
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
protected $commands = [
|
||||
\App\Console\Commands\GenerateDomainFavicons::class,
|
||||
];
|
||||
|
||||
/**
|
||||
* Define the application's command schedule.
|
||||
*/
|
||||
protected function schedule(Schedule $schedule): void
|
||||
{
|
||||
// $schedule->command('inspire')->hourly();
|
||||
}
|
||||
|
||||
/**
|
||||
* Register the commands for the application.
|
||||
*/
|
||||
protected function commands(): void
|
||||
{
|
||||
$this->load(__DIR__.'/Commands');
|
||||
|
||||
require base_path('routes/console.php');
|
||||
}
|
||||
}
|
||||
117
app/Helpers/ThemeHelper.php
Normal file
117
app/Helpers/ThemeHelper.php
Normal file
|
|
@ -0,0 +1,117 @@
|
|||
<?php
|
||||
|
||||
namespace App\Helpers;
|
||||
|
||||
class ThemeHelper
|
||||
{
|
||||
/**
|
||||
* Get the logo path based on the current theme
|
||||
*/
|
||||
public static function getLogoPath(string $type = 'positive'): string
|
||||
{
|
||||
$theme = config('app.theme', 'pr-copilot');
|
||||
|
||||
$logoMap = [
|
||||
'portal' => [
|
||||
'positive' => 'img/logos/portal-logo-positive.svg',
|
||||
'negative' => 'img/logos/portal-logo-negative.svg'
|
||||
],
|
||||
'presseecho' => [
|
||||
'positive' => 'img/logos/presseecho-logo-positiv.svg',
|
||||
'negative' => 'img/logos/presseecho-logo-negativ.svg'
|
||||
],
|
||||
'businessportal24' => [
|
||||
'positive' => 'img/logos/businessportal24-logo-positiv.svg',
|
||||
'negative' => 'img/logos/businessportal24-logo-negativ.svg'
|
||||
]
|
||||
];
|
||||
|
||||
return $logoMap[$theme][$type] ?? $logoMap['pr-copilot'][$type];
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the favicon path based on the current theme
|
||||
*/
|
||||
public static function getFaviconPath(): string
|
||||
{
|
||||
$theme = config('app.theme', 'portal');
|
||||
return "img/favicons/{$theme}-favicon.ico";
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the CSS theme file based on the current theme
|
||||
*/
|
||||
public static function getThemeCssPath(): string
|
||||
{
|
||||
$theme = config('app.theme', 'portal');
|
||||
return "resources/css/web/theme-{$theme}.css";
|
||||
}
|
||||
|
||||
/**
|
||||
* Get domain-specific configuration
|
||||
*/
|
||||
public static function getDomainConfig(): array
|
||||
{
|
||||
$theme = config('app.theme', 'portal');
|
||||
return config("domains.domains.{$theme}", []);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get primary color for the current theme
|
||||
*/
|
||||
public static function getPrimaryColor(): string
|
||||
{
|
||||
$config = self::getDomainConfig();
|
||||
return $config['color_scheme']['primary'] ?? '#526266';
|
||||
}
|
||||
|
||||
/**
|
||||
* Get secondary color for the current theme
|
||||
*/
|
||||
public static function getSecondaryColor(): string
|
||||
{
|
||||
$config = self::getDomainConfig();
|
||||
return $config['color_scheme']['secondary'] ?? '#82a0a7';
|
||||
}
|
||||
|
||||
/**
|
||||
* Get primary font family for the current theme
|
||||
*/
|
||||
public static function getFont(): string
|
||||
{
|
||||
$config = self::getDomainConfig();
|
||||
return $config['font'] ?? 'Montserrat';
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Get domain name for the current theme
|
||||
*/
|
||||
public static function getDomainName(): string
|
||||
{
|
||||
$config = self::getDomainConfig();
|
||||
return $config['domain_name'] ?? 'pr-copilot.test';
|
||||
}
|
||||
|
||||
public static function getDomainUrl(): string
|
||||
{
|
||||
$config = self::getDomainConfig();
|
||||
return $config['url'] ?? config('app.url');
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the asset URL (Vite dev server) for the current theme
|
||||
*/
|
||||
public static function getAssetUrl(): string
|
||||
{
|
||||
$theme = config('app.theme', 'portal');
|
||||
|
||||
$assetUrlMap = [
|
||||
'portal' => 'https://assets.pr-copilot.test',
|
||||
'presseecho' => 'https://assets.presseecho.test',
|
||||
'businessportal24' => 'https://assets.businessportal24.test',
|
||||
];
|
||||
|
||||
return $assetUrlMap[$theme] ?? 'https://assets.pr-copilot.test';
|
||||
}
|
||||
}
|
||||
30
app/Http/Controllers/Auth/VerifyEmailController.php
Normal file
30
app/Http/Controllers/Auth/VerifyEmailController.php
Normal file
|
|
@ -0,0 +1,30 @@
|
|||
<?php
|
||||
|
||||
namespace App\Http\Controllers\Auth;
|
||||
|
||||
use App\Http\Controllers\Controller;
|
||||
use Illuminate\Auth\Events\Verified;
|
||||
use Illuminate\Foundation\Auth\EmailVerificationRequest;
|
||||
use Illuminate\Http\RedirectResponse;
|
||||
|
||||
class VerifyEmailController extends Controller
|
||||
{
|
||||
/**
|
||||
* Mark the authenticated user's email address as verified.
|
||||
*/
|
||||
public function __invoke(EmailVerificationRequest $request): RedirectResponse
|
||||
{
|
||||
if ($request->user()->hasVerifiedEmail()) {
|
||||
return redirect()->intended(route('dashboard', absolute: false).'?verified=1');
|
||||
}
|
||||
|
||||
if ($request->user()->markEmailAsVerified()) {
|
||||
/** @var \Illuminate\Contracts\Auth\MustVerifyEmail $user */
|
||||
$user = $request->user();
|
||||
|
||||
event(new Verified($user));
|
||||
}
|
||||
|
||||
return redirect()->intended(route('dashboard', absolute: false).'?verified=1');
|
||||
}
|
||||
}
|
||||
8
app/Http/Controllers/Controller.php
Normal file
8
app/Http/Controllers/Controller.php
Normal file
|
|
@ -0,0 +1,8 @@
|
|||
<?php
|
||||
|
||||
namespace App\Http\Controllers;
|
||||
|
||||
abstract class Controller
|
||||
{
|
||||
//
|
||||
}
|
||||
22
app/Livewire/Actions/Logout.php
Normal file
22
app/Livewire/Actions/Logout.php
Normal file
|
|
@ -0,0 +1,22 @@
|
|||
<?php
|
||||
|
||||
namespace App\Livewire\Actions;
|
||||
|
||||
use Illuminate\Support\Facades\Auth;
|
||||
use Illuminate\Support\Facades\Session;
|
||||
|
||||
class Logout
|
||||
{
|
||||
/**
|
||||
* Log the current user out of the application.
|
||||
*/
|
||||
public function __invoke()
|
||||
{
|
||||
Auth::guard('web')->logout();
|
||||
|
||||
Session::invalidate();
|
||||
Session::regenerateToken();
|
||||
|
||||
return redirect('/');
|
||||
}
|
||||
}
|
||||
62
app/Models/User.php
Normal file
62
app/Models/User.php
Normal file
|
|
@ -0,0 +1,62 @@
|
|||
<?php
|
||||
|
||||
namespace App\Models;
|
||||
|
||||
// use Illuminate\Contracts\Auth\MustVerifyEmail;
|
||||
use Illuminate\Database\Eloquent\Factories\HasFactory;
|
||||
use Illuminate\Foundation\Auth\User as Authenticatable;
|
||||
use Illuminate\Notifications\Notifiable;
|
||||
use Illuminate\Support\Str;
|
||||
use Laravel\Fortify\TwoFactorAuthenticatable;
|
||||
use Laravel\Sanctum\HasApiTokens;
|
||||
use Spatie\Permission\Traits\HasRoles;
|
||||
class User extends Authenticatable
|
||||
{
|
||||
/** @use HasFactory<\Database\Factories\UserFactory> */
|
||||
use HasApiTokens, HasFactory, Notifiable, HasRoles, TwoFactorAuthenticatable;
|
||||
|
||||
/**
|
||||
* The attributes that are mass assignable.
|
||||
*
|
||||
* @var list<string>
|
||||
*/
|
||||
protected $fillable = [
|
||||
'name',
|
||||
'email',
|
||||
'password',
|
||||
];
|
||||
|
||||
/**
|
||||
* The attributes that should be hidden for serialization.
|
||||
*
|
||||
* @var list<string>
|
||||
*/
|
||||
protected $hidden = [
|
||||
'password',
|
||||
'remember_token',
|
||||
];
|
||||
|
||||
/**
|
||||
* Get the attributes that should be cast.
|
||||
*
|
||||
* @return array<string, string>
|
||||
*/
|
||||
protected function casts(): array
|
||||
{
|
||||
return [
|
||||
'email_verified_at' => 'datetime',
|
||||
'password' => 'hashed',
|
||||
];
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the user's initials
|
||||
*/
|
||||
public function initials(): string
|
||||
{
|
||||
return Str::of($this->name)
|
||||
->explode(' ')
|
||||
->map(fn (string $name) => Str::of($name)->substr(0, 1))
|
||||
->implode('');
|
||||
}
|
||||
}
|
||||
51
app/Providers/AppServiceProvider.php
Normal file
51
app/Providers/AppServiceProvider.php
Normal file
|
|
@ -0,0 +1,51 @@
|
|||
<?php
|
||||
|
||||
namespace App\Providers;
|
||||
|
||||
use Illuminate\Support\Facades\URL;
|
||||
use Illuminate\Support\ServiceProvider;
|
||||
|
||||
class AppServiceProvider extends ServiceProvider
|
||||
{
|
||||
/**
|
||||
* Register any application services.
|
||||
*/
|
||||
public function register(): void
|
||||
{
|
||||
//
|
||||
}
|
||||
|
||||
/**
|
||||
* Bootstrap any application services.
|
||||
*/
|
||||
public function boot(): void
|
||||
{
|
||||
// Force HTTPS when running behind a proxy (like Traefik)
|
||||
if ($this->app->environment('local', 'production')) {
|
||||
URL::forceScheme('https');
|
||||
}
|
||||
|
||||
// Trust proxies for correct request detection
|
||||
$this->app['request']->server->set('HTTPS', 'on');
|
||||
|
||||
// Set dynamic asset URL based on current domain/theme
|
||||
// This is needed for Vite to use the correct asset subdomain
|
||||
//config(['app.asset_url' => 'https://assets.businessportal24.test']);
|
||||
|
||||
//$this->setDynamicAssetUrl();
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the asset URL dynamically based on the current theme
|
||||
*/
|
||||
protected function setDynamicAssetUrl(): void
|
||||
{
|
||||
try {
|
||||
$assetUrl = \App\Helpers\ThemeHelper::getAssetUrl();
|
||||
config(['app.asset_url' => $assetUrl]);
|
||||
} catch (\Exception $e) {
|
||||
// Fallback to default if theme detection fails
|
||||
config(['app.asset_url' => 'https://assets.pr-copilot.test']);
|
||||
}
|
||||
}
|
||||
}
|
||||
52
app/Providers/FortifyServiceProvider.php
Normal file
52
app/Providers/FortifyServiceProvider.php
Normal file
|
|
@ -0,0 +1,52 @@
|
|||
<?php
|
||||
|
||||
namespace App\Providers;
|
||||
|
||||
use App\Actions\Fortify\CreateNewUser;
|
||||
use App\Actions\Fortify\ResetUserPassword;
|
||||
use App\Actions\Fortify\UpdateUserPassword;
|
||||
use App\Actions\Fortify\UpdateUserProfileInformation;
|
||||
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\Actions\RedirectIfTwoFactorAuthenticatable;
|
||||
use Laravel\Fortify\Fortify;
|
||||
use Livewire\Volt\Volt;
|
||||
|
||||
class FortifyServiceProvider extends ServiceProvider
|
||||
{
|
||||
/**
|
||||
* Register any application services.
|
||||
*/
|
||||
public function register(): void
|
||||
{
|
||||
//
|
||||
}
|
||||
|
||||
/**
|
||||
* Bootstrap any application services.
|
||||
*/
|
||||
public function boot(): void
|
||||
{
|
||||
// Nur die notwendigen Fortify-Aktionen behalten
|
||||
Fortify::createUsersUsing(CreateNewUser::class);
|
||||
Fortify::updateUserProfileInformationUsing(UpdateUserProfileInformation::class);
|
||||
Fortify::updateUserPasswordsUsing(UpdateUserPassword::class);
|
||||
Fortify::resetUserPasswordsUsing(ResetUserPassword::class);
|
||||
Fortify::redirectUserForTwoFactorAuthenticationUsing(RedirectIfTwoFactorAuthenticatable::class);
|
||||
|
||||
// Keine View-Definitionen, da wir Volt verwenden
|
||||
|
||||
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'));
|
||||
});
|
||||
}
|
||||
}
|
||||
90
app/Providers/ThemeServiceProvider.php
Normal file
90
app/Providers/ThemeServiceProvider.php
Normal file
|
|
@ -0,0 +1,90 @@
|
|||
<?php
|
||||
|
||||
namespace App\Providers;
|
||||
|
||||
use Illuminate\Support\Facades\Request;
|
||||
use Illuminate\Support\Facades\View;
|
||||
use Illuminate\Support\Facades\Vite;
|
||||
use Illuminate\Support\ServiceProvider;
|
||||
|
||||
class ThemeServiceProvider extends ServiceProvider
|
||||
{
|
||||
/**
|
||||
* Register services.
|
||||
*/
|
||||
public function register(): void
|
||||
{
|
||||
// Registriere die domains.php Konfigurationsdatei
|
||||
$this->mergeConfigFrom(
|
||||
base_path('config/domains.php'),
|
||||
'domains'
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Bootstrap services.
|
||||
*/
|
||||
public function boot(): void
|
||||
{
|
||||
$host = Request::getHost(); // is domain_name
|
||||
$themeOverride = Request::get('theme'); // Allow theme override via URL parameter
|
||||
|
||||
// Standard-Werte für Domain, die nicht in der Konfiguration sind
|
||||
$domainConfig = [
|
||||
'name' => config('app.name'),
|
||||
'theme' => 'portal',
|
||||
'view_prefix' => 'portal',
|
||||
'assets_dir' => 'build/portal',
|
||||
'url' => config('app.url'),
|
||||
'domain_name' => config('app.domain_name'),
|
||||
];
|
||||
|
||||
// Lade die Domain-Konfiguration
|
||||
$confiDomains = config('domains.domains');
|
||||
|
||||
// Suche nach der aktuellen Domain in der Konfiguration
|
||||
foreach ($confiDomains as $name => $config) {
|
||||
if (is_array($config) && isset($config['domain_name']) && $config['domain_name'] === $host) {
|
||||
$domainConfig = array_merge($domainConfig, $config);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
// Allow theme override via URL parameter (for testing)
|
||||
if ($themeOverride && isset($confiDomains[$themeOverride])) {
|
||||
$domainConfig = array_merge($domainConfig, $confiDomains[$themeOverride]);
|
||||
}
|
||||
|
||||
// Grundlegende Konfiguration im Anwendungskontext verfügbar machen
|
||||
config([
|
||||
'app.theme' => $domainConfig['theme'],
|
||||
'app.view_prefix' => $domainConfig['view_prefix'],
|
||||
'app.domain_name' => $domainConfig['domain_name'],
|
||||
'app.url' => $domainConfig['url'],
|
||||
'app.asset_url' => $domainConfig['asset_url'] ?? $domainConfig['url'],
|
||||
]);
|
||||
|
||||
// Spezifischere Daten für die Views verfügbar machen
|
||||
View::share('theme', $domainConfig['theme']);
|
||||
View::share('viewPrefix', $domainConfig['view_prefix']);
|
||||
View::share('domainName', $domainConfig['domain_name']);
|
||||
View::share('domainConfig', $domainConfig);
|
||||
View::share('domainUrl', $domainConfig['url']);
|
||||
|
||||
// Vite-Assets-Konfiguration für die aktuelle Domain
|
||||
if (! app()->runningInConsole() && isset($domainConfig['assets_dir'])) {
|
||||
Vite::useBuildDirectory($domainConfig['assets_dir']);
|
||||
|
||||
// Setze die Asset-URL für Vite Dev-Server
|
||||
if (isset($domainConfig['asset_url'])) {
|
||||
// Erstelle temporäre Hot-File mit der richtigen Asset-URL
|
||||
$hotFile = public_path('hot');
|
||||
if (file_exists($hotFile)) {
|
||||
file_put_contents($hotFile, $domainConfig['asset_url']);
|
||||
}
|
||||
|
||||
Vite::useHotFile($hotFile);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
28
app/Providers/VoltServiceProvider.php
Normal file
28
app/Providers/VoltServiceProvider.php
Normal file
|
|
@ -0,0 +1,28 @@
|
|||
<?php
|
||||
|
||||
namespace App\Providers;
|
||||
|
||||
use Illuminate\Support\ServiceProvider;
|
||||
use Livewire\Volt\Volt;
|
||||
|
||||
class VoltServiceProvider extends ServiceProvider
|
||||
{
|
||||
/**
|
||||
* Register services.
|
||||
*/
|
||||
public function register(): void
|
||||
{
|
||||
//
|
||||
}
|
||||
|
||||
/**
|
||||
* Bootstrap services.
|
||||
*/
|
||||
public function boot(): void
|
||||
{
|
||||
Volt::mount([
|
||||
config('livewire.view_path', resource_path('views/livewire')),
|
||||
resource_path('views/pages'),
|
||||
]);
|
||||
}
|
||||
}
|
||||
Loading…
Add table
Add a link
Reference in a new issue