middleware(['auth', 'verified', BlockAdminDuringImpersonation::class, EnsureUserIsAdmin::class, LogSlowAdminRequests::class]) ->name('dashboard'); // ======================================== // Nutzer-eigene Einstellungen (kein Admin erforderlich) // ======================================== Route::middleware(['auth', 'verified'])->group(function () { Route::redirect('settings', '/admin/me/profile'); Route::redirect('settings/profile', '/admin/me/profile')->name('settings.profile'); Route::livewire('settings/password', 'settings.password')->name('settings.password'); Route::livewire('settings/appearance', 'settings.appearance')->name('settings.appearance'); }); Route::post('admin/impersonate/leave', LeaveImpersonationController::class) ->middleware(['auth', 'throttle:10,1']) ->name('admin.impersonate.leave'); // ======================================== // Admin-Bereich (nur Rollen admin/editor) // ======================================== Route::middleware(['auth', 'verified', BlockAdminDuringImpersonation::class, EnsureUserIsAdmin::class, LogSlowAdminRequests::class])->group(function () { // Content Management Route::livewire('admin/press-releases', 'admin.press-releases.index')->name('admin.press-releases.index'); Route::livewire('admin/press-releases/create', 'admin.press-releases.create')->name('admin.press-releases.create'); Route::livewire('admin/press-releases/{id}', 'admin.press-releases.show')->name('admin.press-releases.show'); Route::livewire('admin/press-releases/{id}/edit', 'admin.press-releases.edit')->name('admin.press-releases.edit'); // Beweisoriginal und veröffentlichte Fassung (Bildkennzeichnung E.4): // nur hier, nur mit Zweck (per POST, Audit A-08), jeder Abruf // protokolliert; Restnachweise entfernter Bilder nur mit // compliance:manage (Audit A-11, im Controller). Route::match(['get', 'post'], 'admin/press-releases/{id}/images/{image}/original', [PressReleaseImageEvidenceController::class, 'original']) ->whereNumber(['id', 'image'])->name('admin.press-releases.images.original'); Route::match(['get', 'post'], 'admin/press-releases/{id}/images/{image}/published', [PressReleaseImageEvidenceController::class, 'published']) ->whereNumber(['id', 'image'])->name('admin.press-releases.images.published'); // Rubriken-Taxonomie: portalweite Struktur — nur admin (Befund H2). Route::middleware('can:categories:manage')->group(function (): void { Route::livewire('admin/categories', 'admin.categories.index')->name('admin.categories.index'); Route::livewire('admin/categories/create', 'admin.categories.create')->name('admin.categories.create'); Route::livewire('admin/categories/{id}/edit', 'admin.categories.edit')->name('admin.categories.edit'); }); Route::middleware('can:footer-codes:manage')->group(function (): void { Route::livewire('admin/footer-codes', 'admin.footer-codes.index')->name('admin.footer-codes.index'); Route::livewire('admin/footer-codes/create', 'admin.footer-codes.create')->name('admin.footer-codes.create'); Route::livewire('admin/footer-codes/{id}/edit', 'admin.footer-codes.edit')->name('admin.footer-codes.edit'); }); // CRM // Lesen (companies:read) auch für Editoren; Schreiben nur admin (companies:write, Befund H2). Route::livewire('admin/companies', 'admin.companies.index')->name('admin.companies.index'); Route::livewire('admin/companies/{id}', 'admin.companies.show')->where('id', '[0-9]+')->name('admin.companies.show'); Route::livewire('admin/contacts', 'admin.contacts.index')->name('admin.contacts.index'); Route::middleware('can:companies:write')->group(function (): void { Route::livewire('admin/companies/create', 'admin.companies.create')->name('admin.companies.create'); Route::livewire('admin/companies/{id}/edit', 'admin.companies.edit')->name('admin.companies.edit'); Route::livewire('admin/companies/{companyId}/contacts/create', 'admin.contacts.create')->name('admin.companies.contacts.create'); Route::livewire('admin/contacts/create', 'admin.contacts.create')->name('admin.contacts.create'); Route::livewire('admin/contacts/{id}/edit', 'admin.contacts.edit')->name('admin.contacts.edit'); }); // Recht & Compliance (WS-3): Queue für DSGVO-/Persönlichkeitsrechts-/Meldungen // — sensible Vorgänge, nur admin (Befund H2). Route::middleware('can:compliance:manage')->group(function (): void { Route::livewire('admin/legal-requests', 'admin.legal-requests.index')->name('admin.legal-requests.index'); Route::livewire('admin/legal-requests/{id}', 'admin.legal-requests.show')->name('admin.legal-requests.show'); // Kontakt: Queue für Anliegen aus dem Kontakt-Widget (Kontaktaufnahme, // Fehlermeldung, PM-Meldung, Feedback) Route::livewire('admin/contact-messages', 'admin.contact-messages.index')->name('admin.contact-messages.index'); Route::livewire('admin/contact-messages/{id}', 'admin.contact-messages.show')->name('admin.contact-messages.show'); }); // Billing: Rechnungen, Zahlungen, Tarife, Gutscheine — nur admin (Befund H2). Route::middleware('can:billing:manage')->group(function (): void { Route::livewire('admin/invoices', 'admin.invoices.index')->name('admin.invoices.index'); Route::get('admin/legacy-invoices/{legacyInvoice}/pdf', LegacyInvoicePdfController::class)->name('admin.legacy-invoices.pdf'); Route::get('admin/invoices/{invoice}/pdf', InvoicePdfController::class)->name('admin.invoices.pdf'); Route::livewire('admin/payments', 'admin.payments.index')->name('admin.payments.index'); Route::livewire('admin/payments/plans', 'admin.payments.plans')->name('admin.payments.plans'); Route::livewire('admin/coupons', 'admin.coupons.index')->name('admin.coupons.index'); Route::livewire('admin/newsletter-sync', 'admin.newsletter.sync')->name('admin.newsletter.sync'); }); // Administration Route::livewire('admin/presets', 'admin.presets.index')->name('admin.presets.index'); Route::livewire('admin/presets/create', 'admin.presets.create')->name('admin.presets.create'); Route::livewire('admin/presets/{id}/edit', 'admin.presets.edit')->name('admin.presets.edit'); Route::livewire('admin/users', 'admin.users')->name('admin.users.index'); Route::livewire('admin/users/create', 'admin.users.create')->name('admin.users.create'); // Gutschriften auf der Detailseite sind finanzwirksam: die Berechtigung // hängt an der Route (persistente Authorize-Middleware, greift auch für // Livewire-Folge-Requests) und nicht nur an mount() (Audit F08). Route::livewire('admin/users/{id}', 'admin.users.show')->middleware('can:users:manage')->name('admin.users.show'); Route::livewire('admin/users/{id}/edit', 'admin.users.edit')->name('admin.users.edit'); // Interner Newsletter: Kampagnen an Bestands-User (Filter + Protokoll). // Massenversand an die User-Datenbank → nur mit users:manage (admin). Route::middleware('can:users:manage')->group(function (): void { // Mail-Sperrliste (Bounce-Suppression, Roadmap 1b.6) Route::livewire('admin/mail/sperrliste', 'admin.mail.suppressions')->name('admin.mail.suppressions'); Route::livewire('admin/newsletter', 'admin.newsletter.campaigns.index')->name('admin.newsletter.campaigns.index'); Route::livewire('admin/newsletter/abmeldungen', 'admin.newsletter.unsubscribes')->name('admin.newsletter.unsubscribes'); Route::livewire('admin/newsletter/create', 'admin.newsletter.campaigns.edit')->name('admin.newsletter.campaigns.create'); Route::livewire('admin/newsletter/{id}', 'admin.newsletter.campaigns.show')->where('id', '[0-9]+')->name('admin.newsletter.campaigns.show'); Route::livewire('admin/newsletter/{id}/edit', 'admin.newsletter.campaigns.edit')->where('id', '[0-9]+')->name('admin.newsletter.campaigns.edit'); }); Route::livewire('admin/roles', 'admin.roles.index')->name('admin.roles.index'); Route::livewire('admin/roles/create', 'admin.roles.create')->name('admin.roles.create'); Route::livewire('admin/roles/{id}/edit', 'admin.roles.edit')->name('admin.roles.edit'); // Bilder (Bildkennzeichnung): alle Titelbilder mit Nachweis, Bestätigung und KI-Vorab-Check. Route::livewire('admin/images', 'admin.images.index')->name('admin.images.index'); // Beweisarchiv (Bildkennzeichnung Block F): Liste, Legal Hold, Öffnen/Export mit Zweck. Route::middleware('can:compliance:manage')->group(function (): void { Route::livewire('admin/archive', 'admin.archive.index')->name('admin.archive.index'); Route::match(['get', 'post'], 'admin/archive/{entry}/download', [ArchiveEntryController::class, 'download'])->whereNumber('entry')->name('admin.archive.download'); Route::match(['get', 'post'], 'admin/archive/{entry}/export', [ArchiveEntryController::class, 'export'])->whereNumber('entry')->name('admin.archive.export'); }); // Reports Route::livewire('admin/reports/activity', 'admin.reports.activity')->name('admin.reports.activity'); Route::livewire('admin/reports/slow-requests', 'admin.reports.slow-requests')->name('admin.reports.slow-requests'); // Einlieferer-Bericht: unzustellbare Kontaktdaten und abgelehnte // Meldungen je API-Einlieferer (Kontakterreichbarkeit). Route::livewire('admin/reports/einlieferer', 'admin.reports.submitter-contacts')->name('admin.reports.submitter-contacts'); // Indexierungsstatus aus der Search Console (Roadmap 1c.4): Verteilung // nach Direkteinreichung/Syndikation — Grundlage für die Kundenansicht. Route::livewire('admin/reports/indexierung', 'admin.reports.index-status')->name('admin.reports.index-status'); Route::middleware('can:settings:manage')->group(function (): void { Route::livewire('admin/reports/system-health', 'admin.reports.system-health')->name('admin.reports.system-health'); }); });