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>
38 lines
1.9 KiB
PHP
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';
|