Certified/routes/web.php
Joël van de Wouw ad2a78a989 Replace email-only sharing with secure share pages
Sharing certificates now creates a Share: a public page behind an
unguessable token where the recipient views and downloads the selected
certificates individually or as a ZIP, instead of receiving links or
attachments by email.

- Share model + certificate_share pivot; expiration (1/7/30 days),
  optional password (encrypted so the owner can re-view it), revocation,
  and open tracking
- Public routes under shared/{token}: password unlock gate (throttled,
  session-scoped), per-certificate download, on-the-fly ZIP that is
  AES-256 encrypted when the share has a password, friendly 410 page
  for expired/revoked links
- Share modal on the certificates index now creates the page and
  reveals a copyable link + password; optional transactional email
  (ShareCreatedMail) still sends the link to a recipient
- New Shares page in the sidebar to copy links, look up passwords,
  revoke, and delete shares
- Old signed-URL controller and both attachment/link mailables removed;
  Dutch translations updated; Pest coverage for the full lifecycle

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-12 15:38:43 +02:00

38 lines
1.9 KiB
PHP

<?php
use App\Http\Controllers\CertificateDownloadController;
use App\Http\Controllers\LanguageController;
use App\Http\Controllers\MarketingController;
use App\Http\Controllers\SharePageController;
use Illuminate\Support\Facades\Route;
Route::get('/', [MarketingController::class, 'home'])->name('home');
Route::get('/security', [MarketingController::class, 'security'])->name('marketing.security');
Route::get('/pricing', [MarketingController::class, 'pricing'])->name('marketing.pricing');
// Available to guests and authenticated users alike.
Route::get('language/{locale}', LanguageController::class)->name('language.switch');
Route::middleware(['auth', 'verified'])->group(function () {
Route::livewire('dashboard', 'pages::dashboard')->name('dashboard');
Route::livewire('certificates', 'pages::certificates.index')->name('certificates.index');
Route::livewire('certificates/create', 'pages::certificates.create')->name('certificates.create');
Route::livewire('certificates/{certificate}/edit', 'pages::certificates.edit')->name('certificates.edit');
Route::get('certificates/{certificate}/download', CertificateDownloadController::class)->name('certificates.download');
Route::livewire('categories', 'pages::categories.index')->name('categories.index');
Route::livewire('shares', 'pages::shares.index')->name('shares.index');
});
// External recipients: authorized by the unguessable share token (and the
// optional share password), not by login.
Route::prefix('shared/{share:token}')->name('shared.')->scopeBindings()->group(function () {
Route::get('/', [SharePageController::class, 'show'])->name('show');
Route::post('unlock', [SharePageController::class, 'unlock'])->middleware('throttle:10,1')->name('unlock');
Route::get('zip', [SharePageController::class, 'zip'])->name('zip');
Route::get('certificates/{certificate}', [SharePageController::class, 'download'])->name('certificate');
});
require __DIR__.'/settings.php';