diff --git a/CHANGELOG.md b/CHANGELOG.md index 289c295..9a2f3f2 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -18,11 +18,14 @@ This project uses Git as the primary rollback trail. The `.old` convention is re - Added search/filter forms for provider, buyer, and job directories. - Added MIT license and first-pass open-source/platform guardrails. - Added deployment documentation covering runtime requirements, ChristIT `/tse` deployment assumptions, and remote change tracking. +- Added optional route base-path config and a checked-in Nginx helper for the ChristIT `/tse` Laravel deployment. ### Verified - `php artisan test` passed with 35 tests and 116 assertions. - `npm run build` passed with a production Vite build. +- `https://christit.com/tse/login` loads publicly. +- Seeded admin login succeeds on `https://christit.com/tse` and reaches `/tse/dashboard`. ### Notes diff --git a/config/app.php b/config/app.php index 423eed5..d0f3903 100644 --- a/config/app.php +++ b/config/app.php @@ -54,6 +54,8 @@ return [ 'url' => env('APP_URL', 'http://localhost'), + 'base_path' => env('APP_BASE_PATH', ''), + /* |-------------------------------------------------------------------------- | Application Timezone diff --git a/docs/DEPLOYMENT.md b/docs/DEPLOYMENT.md index c2fef35..5e90f4e 100644 --- a/docs/DEPLOYMENT.md +++ b/docs/DEPLOYMENT.md @@ -57,7 +57,8 @@ Chosen deployment shape: Initial test data/runtime: - SQLite database at `/sites/provider-exchange/database/database.sqlite`. -- `APP_URL=https://christit.com/tse`. +- `APP_URL=https://christit.com`. +- `APP_BASE_PATH=` for the current Nginx setup, because Nginx passes `SCRIPT_NAME=/tse/index.php` and Laravel derives the request base from that. - `ASSET_URL=https://christit.com/tse`. - `SESSION_PATH=/tse`. - `SESSION_DRIVER=database`. @@ -76,6 +77,17 @@ Initial test data/runtime: - Uploaded app source to `/home/jbird/provider-exchange-release`. - Installed app source into `/sites/provider-exchange`. - Remote Composer was not available in noninteractive SSH; uploaded the local `vendor/` tree for this temporary test deployment. -- No Nginx changes have been applied yet in this deployment pass. +- Installed `php8.5-sqlite3` so the isolated SQLite test database can run under the existing PHP 8.5 runtime. +- Added a narrow `/tse` Nginx block to `/etc/nginx/sites-available/christit.com`. +- Nginx backups created by the helper include: + - `/etc/nginx/sites-available/christit.com.provider-exchange-tse-20260710-115810.bak` + - `/etc/nginx/sites-available/christit.com.provider-exchange-tse-20260710-120003.bak` + - `/etc/nginx/sites-available/christit.com.provider-exchange-tse-20260710-120209.bak` + - `/etc/nginx/sites-available/christit.com.provider-exchange-tse-20260710-121044.bak` + - `/etc/nginx/sites-available/christit.com.provider-exchange-tse-20260710-121151.bak` +- Confirmed `nginx -t` passes after the `/tse` block was applied. +- Confirmed `https://christit.com/tse/` redirects to `https://christit.com/tse/jobs`. +- Confirmed `https://christit.com/tse/login` loads with a single-prefix form action. +- Confirmed seeded admin login succeeds and redirects to `https://christit.com/tse/dashboard`. - No PHP-FPM version changes have been made. - No database server changes have been made. diff --git a/routes/web.php b/routes/web.php index 8cfedee..8cdfc08 100644 --- a/routes/web.php +++ b/routes/web.php @@ -19,6 +19,9 @@ use App\Http\Controllers\WorkOrderMessageController; use App\Http\Controllers\WorkOrderController; use Illuminate\Support\Facades\Route; +$basePath = trim((string) config('app.base_path', ''), '/'); + +Route::prefix($basePath)->group(function (): void { Route::get('/', function () { return view('welcome'); }); @@ -80,3 +83,4 @@ Route::get('/jobs', [JobPostController::class, 'index'])->name('jobs.index'); Route::get('/jobs/{job}', [JobPostController::class, 'show'])->name('jobs.show'); require __DIR__.'/auth.php'; +}); diff --git a/scripts/deploy-christit-tse-nginx.py b/scripts/deploy-christit-tse-nginx.py new file mode 100644 index 0000000..bd1b7b4 --- /dev/null +++ b/scripts/deploy-christit-tse-nginx.py @@ -0,0 +1,73 @@ +#!/usr/bin/env python3 +from datetime import datetime +from pathlib import Path + + +CONF = Path("/etc/nginx/sites-available/christit.com") +MARKER = " client_max_body_size 12G;\n" +STAMP = datetime.now().strftime("%Y%m%d-%H%M%S") + +BLOCK = r''' + + # Technical Services Exchange Laravel test app. + # App root: /sites/provider-exchange; only /public is exposed. + location = /tse { + return 301 /tse/; + } + + location = /tse/ { + return 302 /tse/jobs; + } + + location = /tse/index.php { + try_files /__missing_tse_index @tse_front_controller; + } + + location @tse_front_controller { + include fastcgi_params; + fastcgi_pass unix:/run/php/php8.5-fpm.sock; + fastcgi_intercept_errors on; + fastcgi_param SCRIPT_FILENAME /sites/provider-exchange/public/index.php; + fastcgi_param DOCUMENT_ROOT /sites/provider-exchange/public; + fastcgi_param SCRIPT_NAME /tse/index.php; + fastcgi_param HTTPS on; + } + + location ^~ /tse/ { + alias /sites/provider-exchange/public/; + try_files $uri $uri/ @tse_front_controller; + } +''' + + +def main() -> None: + text = CONF.read_text() + + if "Technical Services Exchange Laravel test app" in text: + start = text.index(" # Technical Services Exchange Laravel test app.") + end = text.index("\n location / {", start) + updated = text[:start] + BLOCK.lstrip("\n") + text[end:] + + if updated == text: + print("NOOP: /tse block already present") + return + + backup = CONF.with_name(f"{CONF.name}.provider-exchange-tse-{STAMP}.bak") + backup.write_text(text) + CONF.write_text(updated) + print(f"UPDATED: {CONF}") + print(f"BACKUP: {backup}") + return + + if MARKER not in text: + raise SystemExit(f"Could not find insertion marker in {CONF}") + + backup = CONF.with_name(f"{CONF.name}.provider-exchange-tse-{STAMP}.bak") + backup.write_text(text) + CONF.write_text(text.replace(MARKER, MARKER + BLOCK, 1)) + print(f"UPDATED: {CONF}") + print(f"BACKUP: {backup}") + + +if __name__ == "__main__": + main()