Commit Graph

1148 Commits

Author SHA1 Message Date
Pierre Arnaud
38ee6fabb4 Only enable SignalR detailed errors in development
Changed EnableDetailedErrors to be conditional based on:
- Debugger attached (DebuggerHelper.IsAttached)
- context.HostingEnvironment.IsDevelopment()

Updated ConfigureServices to accept WebHostBuilderContext to properly
access the hosting environment instead of directly reading environment
variables. This prevents detailed error messages from being exposed in
production builds, which is important for security.
2026-01-31 10:04:29 +01:00
Pierre Arnaud
0ee2bbe31c Refactor debugger detection to use shared DebuggerHelper
Created a new DebuggerHelper class with Lazy<bool> initialization that is
shared by both LaunchOrderDetector and UnpackagedDetector. This eliminates
code duplication and provides a cleaner, more maintainable solution.

- Added DebuggerHelper.cs with lazy-initialized IsAttached property
- Refactored LaunchOrderDetector to use DebuggerHelper
- Refactored UnpackagedDetector to use DebuggerHelper
- Removed nullable bool pattern in favor of Lazy<bool>
2026-01-31 09:38:26 +01:00
Pierre Arnaud
a88e10bbf2 Measure total startup time 2026-01-31 09:18:24 +01:00
Pierre Arnaud
39c7e61ae5 Auto-quit Electron when pipe to .NET process breaks
When an EPIPE error is detected (indicating the .NET process has terminated),
Electron now automatically quits gracefully instead of just suppressing the error.
This ensures the Electron window doesn't remain open after the backend dies.

Uses setImmediate to allow any pending operations to complete before quitting,
and includes a flag to prevent multiple quit attempts.
2026-01-31 09:17:09 +01:00
Pierre Arnaud
96c454aedb Add process-level EPIPE error handlers
The previous fix using try-catch wasn't sufficient because EPIPE errors
are thrown asynchronously at the socket level when writing to stdout/stderr.
Added error event handlers to process.stdout and process.stderr to catch
and suppress EPIPE errors at the source, preventing the error dialog entirely.
2026-01-31 09:10:48 +01:00
Pierre Arnaud
5a77284610 Fix EPIPE error when .NET process terminates before Electron
- Added SafeLogger class to wrap SignalR logging and prevent EPIPE errors
- Enhanced safeConsole wrapper to include warn method
- Replaced all console calls in SignalRBridge with safeConsole
- Configured SignalR to use custom SafeLogger instead of default ConsoleLogger

This prevents the 'broken pipe, write' error dialog when the .NET process
is killed before the Electron process can cleanly shut down.
2026-01-31 09:04:58 +01:00
Pierre Arnaud
0a23659196 Phase 1b: Optimize startup detection for faster .NET initialization
Performance optimizations to startup detection:
- Reduced GatherBuildInfo: >1000ms → 7ms (99% faster!)
- Reduced CollectProcessData: →  86ms
- Reduced DetectAppTypeAndStartup: → 91ms
- Total StartupManager.Initialize: → 91ms (down from >1 second)

Electron startup also improved significantly:
- Module loading: 719ms → 108ms (85% faster!)
- SignalR connection: 884ms → 403ms (54% faster!)
- Total Electron startup: 2.3s → 700ms (70% faster!)

Optimizations applied:

1. Directory checks (UnpackagedDetector):
   - Changed GetDirectories().Any() to direct Directory.Exists()
   - Avoids expensive directory enumeration
   - Saves ~200-500ms on slow disks

2. Debugger state caching:
   - Cache Debugger.IsAttached result in static field
   - Avoids multiple expensive debugger checks
   - Used by both UnpackagedDetector and LaunchOrderDetector
   - Saves ~50-100ms per check

3. Added timing measurements:
   - Added Stopwatch to StartupManager.Initialize()
   - Detailed timing for each initialization phase
   - Visibility into startup performance

Results:
- .NET startup detection: >1000ms → 91ms
- Electron module loading: 719ms → 108ms
- SignalR connection: 884ms → 403ms
- **Total improvement: ~70% faster startup**

The combination of faster directory checks, cached debugger state,
and parallel module loading (from previous commit) results in
sub-second startup times in development mode.
2026-01-30 23:02:13 +01:00
Pierre Arnaud
5d224568d0 Phase 1: Implement parallel module loading for faster startup
Performance optimization:
- Refactored 18 sequential require() calls to load in parallel
- Use Promise.all() to load all API modules simultaneously
- Added comprehensive startup timing measurements

Timing breakdown (before → after):
- Module loading: ~900-1200ms → 719ms (20-40% faster)
- Total Electron startup: Measured at 2.3 seconds
- SignalR connection: 884ms
- Host ready signal: 33ms

Implementation:
- Load all modules in parallel using Promise.resolve().then()
- Organize modules by priority (critical, secondary, utility)
- Maintain backward compatibility with global variable assignments
- Add console.time() measurements for each phase

Benefits:
- Faster perceived startup time
- Non-blocking module initialization
- Clear visibility into startup bottlenecks
- Foundation for future lazy loading optimizations

This is the first phase of startup optimization, targeting the
biggest bottleneck (sequential module loading) with the least effort.
2026-01-30 22:56:37 +01:00
Pierre Arnaud
1c0b9378d2 Phase 6: Add comprehensive authentication documentation
Created new authentication guide:
- docs/SignalR-Authentication-Guide.md (500+ lines)
- Complete threat model and security architecture
- Flow diagrams and implementation details
- Troubleshooting guide with common issues
- FAQ covering design decisions and usage

Updated SignalR implementation summary:
- Added authentication & security section
- Documented token flow and cookie management
- Security properties and protection scope
- Logging & monitoring guidelines
- Multi-user testing procedures
- Updated file changes summary and success metrics

Documentation includes:
- Architecture diagrams (ASCII art)
- Code examples for all components
- Step-by-step authentication flow
- Security considerations and rationale
- Common troubleshooting scenarios
- Testing recommendations
2026-01-30 22:39:45 +01:00
Pierre Arnaud
c12a706289 Phase 5.2: Add comprehensive logging and error handling for authentication
Middleware logging:
- Log successful authentication with cookie setting
- Log failed authentication attempts with path and remote IP
- Log token prefix (first 8 chars) for invalid tokens, never full token
- Added structured logging with ILogger<T>

Electron error handling:
- Detect 401 authentication errors in SignalR connection
- Provide helpful error message about --authtoken parameter
- Differentiate auth errors from other connection failures

Documentation:
- Added XML comments explaining security model
- Documented token generation rationale (128-bit entropy)
- Clarified middleware validation flow in comments

Security:
- Never log full token values
- Generic error messages to prevent information leakage
- Failed auth attempts logged for security monitoring
2026-01-30 22:37:37 +01:00
Pierre Arnaud
8cc3fe4fd7 Phase 5.1: Fix quit handler to support both Socket.IO and SignalR modes
- Added checks for undefined variables before cleanup
- Socket.IO resources (server, apiProcess, io) only cleaned up in legacy mode
- SignalR connection properly stopped in SignalR mode
- Improved error messages to identify which cleanup failed
- Prevents 'Cannot read properties of undefined' error on app quit
2026-01-30 22:36:18 +01:00
Pierre Arnaud
893de1510d Phase 3: Pass authentication token to SignalR connection URL
- Modified main.js to pass authToken to SignalRBridge constructor
- Updated signalr-bridge.js to append token to hub URL
- Removed skip logic for negotiate endpoint in middleware
- SignalR negotiate request now includes token for authentication
- Cookie is set after successful negotiate for subsequent requests

This enables SignalR connections to authenticate using the same
token-based mechanism as HTTP requests, completing the authentication
flow for both Blazor pages and SignalR hub communication.
2026-01-30 22:35:39 +01:00
Pierre Arnaud
6f49a663ea Phase 4: Inject authentication service into RuntimeController and set token 2026-01-30 19:20:03 +01:00
Pierre Arnaud
5b9e2b8b3b Phase 2.1: Register authentication services and middleware in Program.cs 2026-01-30 19:19:23 +01:00
Pierre Arnaud
dee640c526 Phase 1.2: Extract token in Electron and append to URL
- Extract authtoken from command-line in main.js

- Store token in global.authToken for access by API modules

- Append token to initial window URL as query parameter

- Update both JS and TS versions of browserWindows

- First request will be: http://localhost:PORT/?token=GUID
2026-01-30 19:14:09 +01:00
Pierre Arnaud
f598fbf5ce Phase 1.1: Generate authentication token in RuntimeController
- Add authenticationToken field to store GUID

- Generate secure token using Guid.NewGuid().ToString('N')

- Pass token to Electron via --authtoken command-line parameter

- Token is 32 hex characters with 128 bits of entropy
2026-01-30 19:13:12 +01:00
Pierre Arnaud
6847520ea8 Remove HSTS and HTTPS redirection for Electron apps
HSTS and HTTPS redirection are designed for public web servers, not
desktop applications:

- ASP.NET Core only listens on http://localhost (local-only)
- No man-in-the-middle risk for same-machine communication
- HTTPS would require certificate setup with no security benefit
- HTTPS overhead slows down local IPC unnecessarily

Electron apps should use plain HTTP for localhost communication.
2026-01-30 17:30:07 +01:00
Pierre Arnaud
75151282ff Remove unnecessary UseWebSockets() call
SignalR's MapHub<T>() automatically enables WebSocket support, making
explicit UseWebSockets() redundant. SignalR also supports fallback
transports (Server-Sent Events, Long Polling) if WebSockets are unavailable.

Updated documentation to reflect this and clarify that WebSockets are
enabled automatically by MapHub().
2026-01-30 17:28:40 +01:00
Pierre Arnaud
17ef6853ab Fix ASP0014 warning: Use top-level route registration for ElectronHub
Changed from app.UseEndpoints() pattern to direct app.MapHub() call,
following modern ASP.NET Core conventions. This removes the analyzer
warning while maintaining the same functionality.
2026-01-30 17:14:14 +01:00
Pierre Arnaud
12f011bc33 Add comprehensive SignalR implementation documentation
Created detailed summary document incorporating:
- All 6 implementation phases (complete)
- Usage examples with code snippets
- Architecture decisions and rationale
- Critical fixes and their solutions
- Blazor Server integration considerations
- Backward compatibility guarantees
- Known limitations and future enhancements
- Success metrics and validation

Document serves as both implementation reference and user guide.
2026-01-30 17:11:57 +01:00
Pierre Arnaud
217fe83334 Add documentation comments to SignalR implementation
Added comprehensive code comments explaining:
- RuntimeControllerAspNetDotnetFirstSignalR: .NET-first startup flow and key differences from Socket.IO
- SignalRFacade: Type conversion handling and event propagation details
- signalr-bridge.js: Socket.IO compatibility layer and arg handling
- main.js: Keep-alive window pattern and SignalR startup sequence

Comments focus on explaining WHY decisions were made, not just WHAT the code does.
2026-01-30 17:08:37 +01:00
Pierre Arnaud
1fc881674d Clean up debug logging from SignalR implementation
Remove excessive console logging that was added during debugging:
- Removed verbose logging from Program.cs (app ready callback steps)
- Removed HTTP request logging middleware
- Cleaned up RuntimeControllerAspNetDotnetFirstSignalR lifecycle logging
- Streamlined ElectronHub connection/event logging
- Simplified SignalRFacade event handling logging
- Reduced JavaScript logging in main.js and signalr-bridge.js
- Reset log levels to Warning for SignalR components in appsettings

Kept only essential error logging and critical state transitions.
Production-ready logging levels maintained.
2026-01-30 17:07:24 +01:00
Pierre Arnaud
6e369aabef Fix static files and CSS asset reference for SignalR sample
- Fix middleware order: UseAntiforgery must be between UseRouting and UseEndpoints
- Add UseStaticFiles() to serve wwwroot content
- Fix scoped CSS bundle reference: use lowercase 'electronnet-samples-blazorsignalr.styles.css' to match generated asset name
- Add HTTP request logging for debugging
- Enable detailed logging for routing and static files in development
2026-01-30 16:58:29 +01:00
Pierre Arnaud
06a332827b Fix window shutdown and URL port for SignalR mode
- Destroy keep-alive window when first real window is created (enables proper window-all-closed behavior)
- Update ElectronNetRuntime.AspNetWebPort with actual port after Kestrel starts (fixes http://localhost:0 issue)
2026-01-30 16:43:53 +01:00
Pierre Arnaud
23f79244ae Fix SignalR event args spreading for Electron handlers
- Changed event handler to receive args as single array parameter
- Spread array elements as individual arguments to match Socket.IO behavior
2026-01-30 16:40:20 +01:00
Pierre Arnaud
6b9187cf6e Fix SignalR bridge communication issues
- Fix duplicate SignalR connection in main.js (removed redundant connect block)
- Fix race condition: Electron now signals 'electron-host-ready' after loading API modules
- Fix type conversion in SignalRFacade for event handlers (handle JsonElement and numeric types)
- Fix SignalR argument mismatch: pass args as array instead of spread, use object[] instead of params
- .NET now waits for electron-host-ready before calling app ready callback
2026-01-30 16:36:27 +01:00
Pierre Arnaud
108ef19a3b CRITICAL FIX: Prevent Electron from quitting in SignalR mode
ROOT CAUSE: Electron quits when app.on('ready') completes with 0 windows.
In SignalR mode, no windows are created immediately, so Electron exits,
triggering ElectronProcess_Stopped and shutting down ASP.NET.

SOLUTION: Create an invisible 1x1 keep-alive window in SignalR mode to
prevent Electron from quitting while waiting for SignalR connection.

Also:
- Make app.on('ready') async and await startSignalRApiBridge()
- Add window-all-closed handler for SignalR mode
- Add extensive debug logging to track lifecycle
- Don't subscribe to electronProcess.Ready in SignalR controller

This fixes the premature shutdown that prevented SignalR connection.
2026-01-30 15:29:48 +01:00
Pierre Arnaud
8c6020e35b WIP: Debugging SignalR connection timeout
- Add WebSockets middleware to ASP.NET pipeline
- Move HTTPS redirect to production only
- Add extensive debug logging in startSignalRApiBridge
- Enable Warning level logging in SignalR client

Issue: signalRBridge.connect() hangs and never resolves
- Connection starts but never completes
- No error messages from SignalR client
- ASP.NET shuts down after timeout
- ElectronHub.OnConnectedAsync never called

Next: Investigate why WebSocket connection doesn't establish
2026-01-30 13:27:03 +01:00
Pierre Arnaud
547e9f1196 Fix: Add safe console wrapper to prevent EPIPE errors
- Wrap all console.log/error calls in try-catch to handle EPIPE
- Disable SignalR client logging (LogLevel.None)
- Prevents Electron crash when console pipes are closed

This fixes the 'EPIPE: broken pipe, write' error that was preventing
the Electron window from displaying.
2026-01-30 13:22:35 +01:00
Pierre Arnaud
4b971af119 Fix: Execute app ready callback in SignalR mode
- Add RunReadyCallback execution when SignalR connects
- This triggers window creation and other app initialization
- Fixes missing variables in main.js (desktopCapturer, electronHostHook, touchBar, shellApi)
- Window now opens successfully in SignalR mode

Known issue: EPIPE console error when logging to closed pipe (to be fixed)
2026-01-30 13:19:49 +01:00
Pierre Arnaud
da8216b292 Implement bidirectional event routing for SignalR mode
- Update signalr-bridge.js to handle .NET→Electron events via 'event' channel
- Add socket.io-compatible .on() and .emit() methods to SignalRBridge
- Update main.js to load all Electron API modules with SignalR bridge
- Update SignalRFacade.Emit() to send events via 'event' channel
- Add ElectronHub.ElectronEvent() to receive Electron→.NET events
- Add SignalRFacade.TriggerEvent() to invoke .NET event handlers
- Remove duplicate ElectronEvent method from hub

This enables full bidirectional communication:
- .NET can call Electron APIs via Emit (e.g., createBrowserWindow)
- Electron can send events back to .NET (e.g., BrowserWindowCreated)
- Event handlers registered via On/Once now work with SignalR
2026-01-30 13:09:55 +01:00
Pierre Arnaud
be609a513e Refactor: Introduce IFacade interface for SocketIO and SignalR facades
- Create IFacade interface defining common API for SocketIO and SignalR
- Update SocketIoFacade to implement IFacade
- Update SignalRFacade to implement IFacade (add Connect no-op)
- Update RuntimeControllerBase.Socket to return IFacade
- Update RuntimeControllerAspNetBase.Socket to return IFacade
- Update RuntimeControllerDotNetFirst.Socket to return IFacade
- Update ElectronNetRuntime.GetSocket() to return IFacade
- Update BridgeConnector.Socket to return IFacade

This enables polymorphic usage of both facades throughout the codebase
and prepares for full Electron API integration with SignalR mode.
2026-01-30 13:03:19 +01:00
Pierre Arnaud
c4a8de6c4e Fix psl dist folder issue by copying from source after npm install 2026-01-30 12:52:40 +01:00
Pierre Arnaud
5b3d5e07ee Add @microsoft/signalr to package.template.json for npm install during build 2026-01-30 12:46:57 +01:00
Pierre Arnaud
9135aff855 Fix electronurl parameter case sensitivity for Electron command line 2026-01-30 12:39:32 +01:00
Pierre Arnaud
e9efb26dff Fix main.js to check SignalR flags first before legacy mode flags 2026-01-30 12:37:12 +01:00
Pierre Arnaud
e29a3bc27a Fix IsUnpackaged extension to include UnpackedDotnetFirstSignalR 2026-01-30 12:34:42 +01:00
Pierre Arnaud
f55abb357c Fix Electron launch: subscribe to ASP.NET Ready event in SignalR controller 2026-01-30 12:26:17 +01:00
Pierre Arnaud
0b92336de2 Fix dynamic port binding: use 127.0.0.1 instead of localhost for port 0 2026-01-30 12:22:07 +01:00
Pierre Arnaud
4c17027039 Add BlazorSignalR sample to solution file 2026-01-30 12:20:08 +01:00
Pierre Arnaud
5d04ab686a Add ElectronNET.Samples.BlazorSignalR - complete sample app for SignalR mode 2026-01-30 12:17:20 +01:00
Pierre Arnaud
de0c02c503 Add comprehensive documentation for SignalR-based startup mode 2026-01-30 12:11:48 +01:00
Pierre Arnaud
054f5b1c4c Complete Phase 5: Add SignalR startup detection and port 0 configuration 2026-01-30 12:10:49 +01:00
Pierre Arnaud
04ec52208a Fix compilation errors - Phase 4 complete (basic structure) 2026-01-30 12:08:43 +01:00
Pierre Arnaud
268b9c90ce Update RuntimeControllerAspNetDotnetFirstSignalR to use SignalRFacade 2026-01-30 12:06:29 +01:00
Pierre Arnaud
cb7d721b7d Add SignalRFacade for SignalR-based API communication 2026-01-30 12:04:33 +01:00
Pierre Arnaud
c1740b53fc Add SignalR client support to Electron Host for new startup modes 2026-01-30 12:02:46 +01:00
Pierre Arnaud
40aed60c7d Add RuntimeControllerAspNetDotnetFirstSignalR for SignalR-based startup 2026-01-30 12:00:27 +01:00
Pierre Arnaud
8ee81f6abd Add ElectronHub and SignalR infrastructure for new startup modes 2026-01-30 11:58:31 +01:00
Pierre Arnaud
7f2ea4839e Add PackagedDotnetFirstSignalR and UnpackedDotnetFirstSignalR startup methods 2026-01-30 11:57:40 +01:00