From 474d98cb4ccb2ceb4c0cd35cb752e27e84c3a22a Mon Sep 17 00:00:00 2001 From: WINBIGFOX Date: Sun, 4 Jan 2026 17:13:46 +0100 Subject: [PATCH 1/2] feat: integrate Laravel Nightwatch support Added `laravel/nightwatch` dependency to composer.json and implemented environment variable and server startup logic for Nightwatch integration. Updated API routes to include Nightwatch middleware for enhanced functionality. --- composer.json | 1 + .../electron/electron-plugin/src/server/php.ts | 14 ++++++++++++++ routes/api.php | 5 +++-- 3 files changed, 18 insertions(+), 2 deletions(-) diff --git a/composer.json b/composer.json index a76d7c8d..43bbb689 100644 --- a/composer.json +++ b/composer.json @@ -40,6 +40,7 @@ "php": "^8.3", "illuminate/contracts": "^10.0|^11.0|^12.0", "laravel/prompts": "^0.1.1|^0.2|^0.3", + "laravel/nightwatch": "^1.21", "nativephp/php-bin": "^1.0", "spatie/laravel-package-tools": "^1.16.4", "symfony/filesystem": "^6.4|^7.2", diff --git a/resources/electron/electron-plugin/src/server/php.ts b/resources/electron/electron-plugin/src/server/php.ts index 5463fdca..cafc9438 100644 --- a/resources/electron/electron-plugin/src/server/php.ts +++ b/resources/electron/electron-plugin/src/server/php.ts @@ -277,6 +277,8 @@ interface EnvironmentVariables { APP_ROUTES_CACHE?: string; APP_EVENTS_CACHE?: string; VIEW_COMPILED_PATH?: string; + + NIGHTWATCH_INGEST_URI?: string; } function getDefaultEnvironmentVariables(secret?: string, apiPort?: number): EnvironmentVariables { @@ -342,6 +344,12 @@ function serveApp(secret, apiPort, phpIniSettings): Promise { const env = getDefaultEnvironmentVariables(secret, apiPort); + let phpNightWatchPort: number | undefined; + if (process.env.NIGHTWATCH_TOKEN) { + phpNightWatchPort = await getPhpPort(); + env.NIGHTWATCH_INGEST_URI = `127.0.0.1:${phpNightWatchPort}`; + } + const phpOptions = { cwd: appPath, env @@ -351,6 +359,12 @@ function serveApp(secret, apiPort, phpIniSettings): Promise { name: 'nativephp', // So it doesn't conflict with settings of the app }); + if(env.NIGHTWATCH_INGEST_URI && phpNightWatchPort) { + console.log('Starting Nightwatch server...'); + callPhp(['artisan', 'nightwatch:agent', `--listen-on=${env.NIGHTWATCH_INGEST_URI}`], phpOptions, phpIniSettings) + console.log('Nightwatch server started on port:', phpNightWatchPort); + } + // Cache the project if (shouldOptimize(store)) { console.log('Caching view and routes...'); diff --git a/routes/api.php b/routes/api.php index c94c2198..d748d44e 100644 --- a/routes/api.php +++ b/routes/api.php @@ -2,14 +2,15 @@ use App\Http\Middleware\VerifyCsrfToken; use Illuminate\Support\Facades\Route; +use Laravel\Nightwatch\Http\Middleware\Sample; use Native\Desktop\Http\Controllers\CreateSecurityCookieController; use Native\Desktop\Http\Controllers\DispatchEventFromAppController; use Native\Desktop\Http\Controllers\NativeAppBootedController; use Native\Desktop\Http\Middleware\PreventRegularBrowserAccess; -Route::group(['middleware' => PreventRegularBrowserAccess::class], function () { +Route::group(['middleware' => [PreventRegularBrowserAccess::class, Sample::never()]], function () { Route::post('_native/api/booted', NativeAppBootedController::class); Route::post('_native/api/events', DispatchEventFromAppController::class); })->withoutMiddleware(VerifyCsrfToken::class); -Route::get('_native/api/cookie', CreateSecurityCookieController::class); +Route::get('_native/api/cookie', CreateSecurityCookieController::class)->middleware(Sample::never()); From e953d7ea334ab5d8a36fd9a2331f8256a3ee9c04 Mon Sep 17 00:00:00 2001 From: WINBIGFOX Date: Thu, 8 Jan 2026 11:54:10 +0100 Subject: [PATCH 2/2] refactor: replace Laravel Nightwatch middleware with OptionalNightwatchNever Removed `laravel/nightwatch` dependency and introduced `OptionalNightwatchNever` middleware to conditionally handle Nightwatch functionality. Updated API routes and server logic to reflect middleware replacement and improve compatibility. --- composer.json | 1 - config/nativephp-internal.php | 1 + .../electron-plugin/src/server/php.ts | 69 ++++++++++++++++++- routes/api.php | 6 +- .../Middleware/OptionalNightwatchNever.php | 20 ++++++ 5 files changed, 91 insertions(+), 6 deletions(-) create mode 100644 src/Http/Middleware/OptionalNightwatchNever.php diff --git a/composer.json b/composer.json index 43bbb689..a76d7c8d 100644 --- a/composer.json +++ b/composer.json @@ -40,7 +40,6 @@ "php": "^8.3", "illuminate/contracts": "^10.0|^11.0|^12.0", "laravel/prompts": "^0.1.1|^0.2|^0.3", - "laravel/nightwatch": "^1.21", "nativephp/php-bin": "^1.0", "spatie/laravel-package-tools": "^1.16.4", "symfony/filesystem": "^6.4|^7.2", diff --git a/config/nativephp-internal.php b/config/nativephp-internal.php index 3d1a10ba..b7108588 100644 --- a/config/nativephp-internal.php +++ b/config/nativephp-internal.php @@ -89,6 +89,7 @@ 'storage/hot', // Only needed for local testing + 'vendor/nativephp/desktop/.git', 'vendor/nativephp/desktop/resources', 'vendor/nativephp/desktop/vendor', 'vendor/nativephp/php-bin', diff --git a/resources/electron/electron-plugin/src/server/php.ts b/resources/electron/electron-plugin/src/server/php.ts index cafc9438..97a585ef 100644 --- a/resources/electron/electron-plugin/src/server/php.ts +++ b/resources/electron/electron-plugin/src/server/php.ts @@ -1,4 +1,4 @@ -import {mkdirSync, statSync, writeFileSync, existsSync} from 'fs' +import { mkdirSync, statSync, writeFileSync, existsSync, readFileSync } from 'fs'; import fs_extra from 'fs-extra'; const {copySync, mkdirpSync} = fs_extra; @@ -49,6 +49,65 @@ function shouldOptimize(store) { // return runningSecureBuild() && store.get('optimized_version') !== app.getVersion(); } +function hasNightwatchInstalled(appPath: string) { + const candidateRoots = [ + appPath, + join(appPath, "build", "__nativephp_app_bundle") + ]; + + for (const root of candidateRoots) { + if (existsSync(join(root, "vendor", "laravel", "nightwatch"))) { + return true; + } + + const composerLock = join(root, "composer.lock"); + + if (!existsSync(composerLock)) { + continue; + } + + try { + if (readFileSync(composerLock, "utf8").includes("\"name\": \"laravel/nightwatch\"")) { + return true; + } + } catch { + // ignore and keep looking + } + } + + return false; +} + +function getNightwatchToken(appPath: string) { + if (process.env.NIGHTWATCH_TOKEN) { + return process.env.NIGHTWATCH_TOKEN; + } + + const candidateRoots = [ + appPath, + join(appPath, "build", "__nativephp_app_bundle") + ]; + + for (const root of candidateRoots) { + const envPath = join(root, ".env"); + + if (!existsSync(envPath)) { + continue; + } + + try { + const content = readFileSync(envPath, "utf8"); + const match = content.match(/^NIGHTWATCH_TOKEN=(.+)$/m); + + if (match && match[1]) { + return match[1].replace(/^['"]|['"]$/g, ""); + } + } catch { + // ignore and keep looking + } + } +} + async function getPhpPort() { // Try get-port first (fast path) const suggestedPort = await getPort({ @@ -278,6 +337,7 @@ interface EnvironmentVariables { APP_EVENTS_CACHE?: string; VIEW_COMPILED_PATH?: string; + NIGHTWATCH_TOKEN?: string; NIGHTWATCH_INGEST_URI?: string; } @@ -344,10 +404,15 @@ function serveApp(secret, apiPort, phpIniSettings): Promise { const env = getDefaultEnvironmentVariables(secret, apiPort); + + const nightwatchToken = getNightwatchToken(appPath); let phpNightWatchPort: number | undefined; - if (process.env.NIGHTWATCH_TOKEN) { + if (nightwatchToken && hasNightwatchInstalled(appPath)) { phpNightWatchPort = await getPhpPort(); + env.NIGHTWATCH_TOKEN = nightwatchToken; env.NIGHTWATCH_INGEST_URI = `127.0.0.1:${phpNightWatchPort}`; + } else if (nightwatchToken) { + console.log("Skipping Nightwatch: package not installed."); } const phpOptions = { diff --git a/routes/api.php b/routes/api.php index d748d44e..02207409 100644 --- a/routes/api.php +++ b/routes/api.php @@ -2,15 +2,15 @@ use App\Http\Middleware\VerifyCsrfToken; use Illuminate\Support\Facades\Route; -use Laravel\Nightwatch\Http\Middleware\Sample; use Native\Desktop\Http\Controllers\CreateSecurityCookieController; use Native\Desktop\Http\Controllers\DispatchEventFromAppController; use Native\Desktop\Http\Controllers\NativeAppBootedController; +use Native\Desktop\Http\Middleware\OptionalNightwatchNever; use Native\Desktop\Http\Middleware\PreventRegularBrowserAccess; -Route::group(['middleware' => [PreventRegularBrowserAccess::class, Sample::never()]], function () { +Route::group(['middleware' => [OptionalNightwatchNever::class, PreventRegularBrowserAccess::class]], function () { Route::post('_native/api/booted', NativeAppBootedController::class); Route::post('_native/api/events', DispatchEventFromAppController::class); })->withoutMiddleware(VerifyCsrfToken::class); -Route::get('_native/api/cookie', CreateSecurityCookieController::class)->middleware(Sample::never()); +Route::get('_native/api/cookie', CreateSecurityCookieController::class)->middleware(OptionalNightwatchNever::class); diff --git a/src/Http/Middleware/OptionalNightwatchNever.php b/src/Http/Middleware/OptionalNightwatchNever.php new file mode 100644 index 00000000..9bd9df06 --- /dev/null +++ b/src/Http/Middleware/OptionalNightwatchNever.php @@ -0,0 +1,20 @@ +handle($request, $next, 0.0); + } + + return $next($request); + } +}