| Server IP : 35.80.110.71 / Your IP : 216.73.216.221 Web Server : Apache/2.4.58 (Ubuntu) System : Linux ip-172-31-21-44 6.17.0-1019-aws #19~24.04.1-Ubuntu SMP Tue Jun 23 18:53:06 UTC 2026 x86_64 User : ubuntu ( 1000) PHP Version : 8.3.31 Disable Function : NONE MySQL : OFF | cURL : ON | WGET : ON | Perl : ON | Python : OFF | Sudo : ON | Pkexec : OFF Directory : /var/www/client-portal-laravel/releases/20260626120707/routes/ |
Upload File : |
<?php
declare(strict_types=1);
/**
* ScopeForged Websites Product Routes
*
* Routes for websites.scopeforged.com. Phase 0 serves a single-page landing
* with a Calendly embed and a lead capture form. Subsequent phases extend
* this group (industry pages, builder, customer portal) per plan 270.
*
* @see config/domains.php for the domain binding
* @see bootstrap/app.php for the route-group registration
* @see docs/plans/website-hosting/270-scopeforged-websites-engineering-plan.md
*/
use App\Http\Controllers\Websites\BuilderController;
use App\Http\Controllers\Websites\BuilderResumeController;
use App\Http\Controllers\Websites\ExampleController;
use App\Http\Controllers\Websites\FaqController;
use App\Http\Controllers\Websites\FeatureController;
use App\Http\Controllers\Websites\HomeController;
use App\Http\Controllers\Websites\IndustryController;
use App\Http\Controllers\Websites\LeadController;
use App\Http\Controllers\Websites\PricingController;
use App\Http\Controllers\Websites\PublicProposalController;
use App\Http\Controllers\Websites\RobotsController;
use App\Http\Controllers\Websites\SitemapController;
use App\Http\Controllers\Websites\WebsiteOnboardingController;
use App\Http\Middleware\ResolveBuilderSession;
use Illuminate\Support\Facades\Route;
Route::get('/', [HomeController::class, 'index'])->name('websites.home');
// Plan 272 §B.5 — tenant-aware throttle on lead capture (anonymous → IP bucket).
Route::post('/leads', [LeadController::class, 'store'])
->middleware('throttle.tenant:10,1')
->name('websites.leads.store');
Route::get('/industries', [IndustryController::class, 'index'])->name('websites.industries.index');
Route::get('/industries/{industry:slug}', [IndustryController::class, 'show'])->name('websites.industries.show');
Route::get('/features', [FeatureController::class, 'index'])->name('websites.features.index');
Route::get('/features/{feature:slug}', [FeatureController::class, 'show'])->name('websites.features.show');
Route::get('/pricing', [PricingController::class, 'index'])->name('websites.pricing');
Route::get('/faq', [FaqController::class, 'index'])->name('websites.faq');
Route::get('/examples', [ExampleController::class, 'index'])->name('websites.examples.index');
Route::get('/examples/{example:slug}', [ExampleController::class, 'show'])->name('websites.examples.show');
Route::get('/sitemap.xml', [SitemapController::class, 'index'])->name('websites.sitemap');
Route::get('/robots.txt', [RobotsController::class, 'index'])->name('websites.robots');
// Builder / configurator wizard (plan 270 §Phase 2).
// Plan 272 §B.5 — tenant-aware throttle on builder writes. Anonymous
// builder sessions key on IP; resumed authenticated sessions key on
// the tenant client id so noisy resume traffic on a shared NAT pool
// doesn't lock customers out of each other's buckets.
Route::middleware([ResolveBuilderSession::class, 'throttle.tenant:120,1'])->prefix('builder')->name('websites.builder.')->group(function () {
Route::get('/', [BuilderController::class, 'start'])->name('start');
Route::post('/industry', [BuilderController::class, 'updateIndustry'])->name('update.industry');
Route::post('/basics', [BuilderController::class, 'updateBasics'])->name('update.basics');
Route::post('/services', [BuilderController::class, 'updateServices'])->name('update.services');
Route::post('/plan', [BuilderController::class, 'updatePlan'])->name('update.plan');
Route::post('/branding', [BuilderController::class, 'updateBranding'])->name('update.branding');
Route::post('/contact', [BuilderController::class, 'updateContact'])->name('update.contact');
Route::post('/logo', [BuilderController::class, 'uploadLogo'])->name('upload.logo');
// Plan 272 §D.1 — CSV service-list import.
Route::post('/import/csv', [BuilderController::class, 'uploadCsv'])->name('upload.csv');
Route::get('/import/{asset}/status', [BuilderController::class, 'importStatus'])->name('import.status');
Route::post('/import/{asset}/apply', [BuilderController::class, 'applyImport'])->name('import.apply');
// Plan 272 §D.2 — Cross-device resume.
Route::post('/resume/request', [BuilderResumeController::class, 'requestLink'])
->middleware('throttle.tenant:5,60')
->name('resume.request');
Route::get('/resume/{token}', [BuilderResumeController::class, 'consume'])
->where('token', '[a-f0-9]{64}')
->name('resume.consume');
Route::post('/submit', [BuilderController::class, 'submit'])
->middleware('idempotent')
->name('submit');
Route::get('/preview/{template}', [BuilderController::class, 'preview'])->name('preview');
Route::get('/{step}', [BuilderController::class, 'show'])
->whereIn('step', ['industry', 'basics', 'services', 'plan', 'branding', 'contact', 'review', 'done'])
->name('show');
});
// Customer-facing proposal page (plan 270 §Phase 3). Token-gated; no auth.
Route::prefix('proposal')->name('websites.proposals.')->group(function () {
Route::get('/{token}', [PublicProposalController::class, 'show'])->name('show');
Route::get('/{token}/pdf', [PublicProposalController::class, 'downloadPdf'])->name('pdf');
Route::post('/{token}/accept', [PublicProposalController::class, 'accept'])
->middleware('idempotent')
->name('accept');
Route::get('/{token}/thank-you', [PublicProposalController::class, 'thankYou'])->name('thank-you');
});
// Customer onboarding handoff (plan 270 §Phase 4). One-time signed link emailed
// after a WebsiteSubscription first becomes Active/Trialing.
Route::prefix('onboarding')->name('websites.onboarding.')->group(function () {
Route::get('/{token}', [WebsiteOnboardingController::class, 'show'])->name('show');
Route::post('/{token}', [WebsiteOnboardingController::class, 'complete'])->name('complete');
});
// CSP violation report sink (plan 270 §Phase 7). Rate-limited per source IP.
Route::post('/csp-report', [\App\Http\Controllers\Websites\CspReportController::class, 'store'])
->middleware('throttle:60,1')
->name('websites.csp.report');