Skip to content

Commit 68af265

Browse files
authored
Remove server-side use of API client. (#40)
* Don't force agent uninstall upon missing entities. * Add toast text to localization. * Handle async lambdas in IPC. * Add optional version input. * Remove ControlrApi from Login.razor * Use UserId instead of email for preference lookup. * Move instructions md. * Extract BaseLayout for use in both. * Remove unused HttpClientConfigurer.
1 parent f7f7741 commit 68af265

40 files changed

+1047
-784
lines changed

.github/instructions/copilot-instructions.md

Lines changed: 286 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,4 +2,289 @@
22
applyTo: '**'
33
---
44

5-
Use the instructions in `AGENTS.md` in the solution root.
5+
# Project Overview
6+
7+
ControlR is a cross-platform solution for remote access and remote control of devices. It primarily uses the latest .NET version for backend web, frontend web (via Blazor), and the desktop applications.
8+
9+
## Quick Start
10+
11+
The easiest way to build and run the application is to use the provided launch profiles in your IDE.
12+
13+
- **Visual Studio / JetBrains Rider:** Use the **"Full Stack"** launch profile to start all the necessary projects for a complete debugging experience.
14+
- **VS Code:**
15+
- Use the **"Full Stack (Debug)"** config in `launch.json` for general debugging.
16+
- Use the **"Full Stack (Hot Reload)"** config for Blazor UI development to get fast feedback on changes.
17+
- **CLI:** To simply build the entire solution, run `dotnet build ControlR.slnx` from the root directory.
18+
19+
## Context Scope
20+
21+
- Do not include any files or folders within `ControlR.Web.Server/novnc/` in your context.
22+
- Do not include anything within folders named `node_modules` in your context.
23+
24+
## High-Level Architecture
25+
26+
The following diagram shows the communication flow between the major components of the system.
27+
28+
```mermaid
29+
graph TD
30+
subgraph "User's Browser"
31+
A[Web Client - Blazor]
32+
end
33+
34+
subgraph "Cloud / Server Host"
35+
B[Web Server - ASP.NET Core]
36+
C[WebSocket Relay]
37+
end
38+
39+
subgraph "Remote Machine"
40+
D[Agent - Background Service]
41+
E[Desktop Client - Avalonia]
42+
end
43+
44+
A -- HTTP & SignalR --> B
45+
B -- SignalR UI Updates --> A
46+
47+
D -- SignalR Heartbeat/Data --> B
48+
B -- SignalR Commands --> D
49+
50+
A -- Remote Control Stream --> C
51+
C -- Remote Control Stream --> E
52+
53+
D -- IPC via Named Pipes --> E
54+
E -- IPC via Named Pipes --> D
55+
```
56+
57+
## Project Structure
58+
59+
### Core Components
60+
61+
- **ControlR.Web.Server** - ASP.NET Core web server with API endpoints and SignalR hubs
62+
- **ControlR.Web.Client** - Blazor WebAssembly frontend using MudBlazor components
63+
- **ControlR.Web.AppHost** - .NET Aspire orchestration host for development
64+
- **ControlR.Web.ServiceDefaults** - Shared service configuration and defaults
65+
- **ControlR.Web.WebSocketRelay** - WebSocket relay service for real-time communication
66+
- **ControlR.Agent** - Background service/daemon that runs on controlled devices
67+
- **ControlR.DesktopClient** - Cross-platform Avalonia UI desktop application
68+
69+
### Platform-Specific Components
70+
71+
- **ControlR.DesktopClient.Windows** - Windows-specific desktop client implementations
72+
- **ControlR.DesktopClient.Linux** - Linux-specific desktop client implementations
73+
- **ControlR.DesktopClient.Mac** - macOS-specific desktop client implementations
74+
- **ControlR.DesktopClient.Common** - Shared desktop client functionality
75+
- **ControlR.Agent.Common** - Shared agent functionality across platforms
76+
77+
### Shared Libraries
78+
79+
- **ControlR.Libraries.Shared** - Core shared models, DTOs, and utilities
80+
- **ControlR.Libraries.DevicesCommon** - Device-related common functionality
81+
- **ControlR.Libraries.DevicesNative** - Native device interaction libraries
82+
- **ControlR.Libraries.Clients** - Client-side shared functionality
83+
- **ControlR.Libraries.ApiClient** - Generated API client for server communication
84+
- **ControlR.Libraries.Ipc** - Inter-process communication utilities
85+
- **ControlR.Libraries.Signalr.Client** - SignalR client abstractions
86+
- **ControlR.Libraries.WebSocketRelay.Common** - WebSocket relay shared components
87+
- **ControlR.Libraries.NativeInterop.Windows** - Windows native interop
88+
- **ControlR.Libraries.NativeInterop.Unix** - Unix/Linux native interop
89+
90+
## Communication Architecture
91+
92+
### SignalR Hub Pattern
93+
94+
- **AgentHub** - Receives device heartbeats and forwards data to ViewerHub groups
95+
- **ViewerHub** - Handles web client connections and remote control requests
96+
- Agents send `DeviceDto` heartbeats via `UpdateDevice()` which triggers real-time UI updates
97+
- Hub groups organize connections by tenant, device tags, and user roles for targeted messaging
98+
99+
### Agent-DesktopClient IPC
100+
101+
- Agent runs as system service/daemon, DesktopClient runs in user sessions
102+
- Communication via named pipes using `IIpcConnection` abstractions
103+
- Agent forwards remote control requests to appropriate DesktopClient via `RemoteControlRequestIpcDto`
104+
- DesktopClient reports back to Agent, which relays to web server via SignalR
105+
106+
## Technology Stack
107+
108+
### Backend
109+
110+
- **.NET 9** - Latest .NET framework
111+
- **ASP.NET Core** - Web framework for APIs and web hosting
112+
- **SignalR** - Real-time communication
113+
- **Entity Framework Core** - ORM for database operations
114+
- **PostgreSQL** - Primary database (with InMemory option for testing)
115+
116+
### Frontend
117+
118+
- **Blazor WebAssembly** - Client-side web UI framework
119+
- **MudBlazor** - Material Design component library
120+
- **JavaScript Interop** - For browser-specific functionality
121+
122+
### Desktop Applications
123+
124+
- **Avalonia UI** - Cross-platform .NET UI framework
125+
- **Multi-targeting** - Supports Windows, Linux, and macOS
126+
- **Native Interop** - Platform-specific functionality via P/Invoke
127+
- **Avalonia Icons** - Avalonia Icons can be retrieved from here: https://avaloniaui.github.io/icons.html. They should be added to the `Icons.axaml` resource dictionary as needed.
128+
- **Localization** - Don't use hardcoded strings in the UI.
129+
- In AXAML, bind to the `Localization` class properties. Example: `Content="{x:Static common:Localization.OkText}"`
130+
- Add keys and values to the JSON files under `/ControlR.DesktopClient.Common/Resources/Strings/`.
131+
132+
## Architecture Patterns
133+
134+
### Web Architecture
135+
136+
- **Clean Architecture** - Separation of concerns with clear dependencies
137+
- **Dependency Injection** - Built-in .NET DI container
138+
139+
### Service Registration
140+
141+
In general, services are not registered directly in `Program.cs`. Instead, extension methods are used to group service registrations for different parts of the application. Here's where you can find the main service registration methods for each project:
142+
143+
- **ControlR.Agent**: `AddControlRAgent` in `ControlR.Agent.Common\Startup\HostBuilderExtensions.cs`
144+
- **ControlR.Web.Server**: `AddControlrServer` in `ControlR.Web.Server\Startup\WebApplicationBuilderExtensions.cs`
145+
- **ControlR.Web.Client**: `AddControlrWebClient` in `ControlR.Web.Client\Startup\IServiceCollectionExtensions.cs`
146+
- **ControlR.DesktopClient**: `AddControlrDesktop` in `ControlR.DesktopClient\StaticServiceProvider.cs`
147+
148+
### Desktop Architecture
149+
150+
- **MVVM Pattern** - Model-View-ViewModel for UI separation
151+
- **Localization** - `Localization.cs` will pull region-specific strings from `/Resources/Strings/{locale}.json`
152+
- All text in the UI should be bound to localization keys using `x:Static`
153+
- **Command Pattern** - For user actions and operations
154+
- **IMessenger** - Cross-component communication
155+
- **Service Layer** - Business logic abstraction
156+
157+
### Cross-Platform Strategy
158+
159+
- **Shared Libraries** - Common functionality across platforms
160+
- **Platform Abstraction** - Interface-based platform-specific implementations
161+
- **Conditional Compilation** - Platform-specific code paths
162+
- **Agent service layout** — The shared library `ControlR.Agent.Common` organizes platform-specific implementations under platform-named `Services` sub-namespaces/folders (for example `.Services.Windows`, `.Services.Linux`, `.Services.Mac`). Interfaces live in the common namespace and the platform implementations are selected via conditional compilation, platform attributes, or at host registration time.
163+
- **DesktopClient per-platform projects** — The Desktop client isolates native UI/OS integrations into separate projects (`ControlR.DesktopClient.Windows`, `.Linux`, `.Mac`) while keeping shared UI and view-model code in `ControlR.DesktopClient.Common`. `ControlR.DesktopClient.csproj` conditionally references or multi-targets the appropriate platform project so only the target-OS code is built and linked. This keeps native UI and OS integrations isolated in their own projects while allowing shared UI and view-model code to remain common.
164+
165+
## Key Features
166+
167+
- **Remote Desktop Control** - Full desktop access and control
168+
- **Screen Sharing** - Real-time screen streaming
169+
- **File Transfer** - Secure file operations between devices
170+
- **Multi-tenancy** - Support for multiple organizations
171+
- **Self-hosting** - Can be deployed on-premises
172+
- **Cross-platform** - Windows, Linux, macOS support
173+
- **Authentication** - Identity Framework with optional OAuth integration (GitHub, Microsoft)
174+
- **Real-time Communication** - SignalR and WebSocket support
175+
176+
## Development Guidelines
177+
178+
### General Coding Standards
179+
180+
- Use 2 spaces for indentation
181+
- Don't try to be overly clever. Code should be easily readable.
182+
- Don't try to fit everything on one line. Use white space for readability.
183+
- **CRITICAL: All code written is production code.** Never write placeholder implementations, TODOs, or comments suggesting the code needs improvement "in production" or is a "simplification". Every implementation must be complete, correct, and production-ready.
184+
- **Never use phrases like:**
185+
- "In production, you should..."
186+
- "This is a simplification..."
187+
- "This assumes..." (unless documenting actual API contracts)
188+
- "This is an estimate..."
189+
- "// TODO:", "// FIXME:", "// HACK:", etc.
190+
- **If you don't know how to implement something correctly:**
191+
- Research the proper implementation
192+
- Ask for clarification
193+
- DO NOT write incomplete code with apologetic comments
194+
- **All implementations must handle:**
195+
- Edge cases appropriately
196+
- Error conditions with proper error handling
197+
- Resource cleanup (dispose patterns, etc.)
198+
- Thread safety where applicable
199+
- Format variations, null checks, boundary conditions
200+
201+
### Build and Task System
202+
203+
- Use the following build command to verify that changes compile: `dotnet build ControlR.slnx --verbosity quiet`
204+
- If successful, there will be no output.
205+
- If unsuccessful, errors will be displayed.
206+
- When a terminal task outputs "Terminal will be reused by tasks, press any key to close it.", interpret this as the task completion signal.
207+
- If no errors were displayed prior to this message, treat the task as successful.
208+
- Do not wait for user interaction or attempt to close the terminal. Proceed immediately to the next step.
209+
- Do not attempt to fix warning `BB0001: Member '{member_name}' is not in the correct order`. You are really bad at fixing this. Just mention it in your summary, and I'll fix it.
210+
211+
### C# Coding Standards
212+
213+
- Use the latest C# language features and default recommendations.
214+
- Use StyleCop conventions when ordering class members.
215+
- Do not use null-forgiving operator (!) outside of tests. Handle null checks appropriately.
216+
- Prefer var of explicit types for variables.
217+
- Reduce indentation by returning/continuing early and inverting conditions when appropriate.
218+
- Always prefer collection expressions to initialize collections (e.g. '[]').
219+
- If an interface only has one implementation, keep the interface and implementation in the same file.
220+
- Do not leave comments that reference historical changes, prior implementations, or what was fixed. Comments should explain current intent only.
221+
- Don't append "Async" suffix to async method names, unless to specifically distinguish from an existing sync method of the same name.
222+
- Open and close braces should be on their own lines.
223+
224+
### Platform-Specific Development
225+
226+
- Use `[SupportedOSPlatform]` attributes for platform-specific code
227+
- Conditional compilation symbols: `WINDOWS_BUILD`, `MAC_BUILD`, `LINUX_BUILD`, `UNIX_BUILD`
228+
- Platform detection via `ISystemEnvironment.Platform` and `RuntimeInformation`
229+
230+
### Web UI Guidelines
231+
232+
- Use MudBlazor components where appropriate for the UI.
233+
- When making changes to `.razor` files, check if there's a code-behind `.cs` file associated with it. If so, add your C# code there, including service injections.
234+
- Prefer using code-behind CS files for Razor components instead of using the `@code {}` block in Razor files.
235+
236+
### Project Organization
237+
238+
- Follow the established folder structure and naming conventions.
239+
- Keep platform-specific code in appropriate platform projects.
240+
- Use shared libraries for common functionality across projects.
241+
- DTOs go under `/Libraries/ControlR.Libraries.Shared/Dtos/`, under their respective namespace.
242+
- `HubDtos` contain DTOs used in SignalR hubs.
243+
- `IpcDtos` contain DTOs used in the IPC connection between `Agent` and `DesktopClient`.
244+
- `ServerApi` contains DTOs used in the REST API.
245+
- `StreamerDtos` contain DTOs used by remote control, which get routed through the websocket relay.
246+
- Maintain clear separation between business logic and UI code.
247+
- **Planning documents and implementation notes** should be placed in the `/.plans/` directory, NOT committed to the main source tree.
248+
- **Do not create random public classes** in files containing other classes. Instead:
249+
- Use existing shared types from `ControlR.Libraries.Shared.Primitives` when available (e.g., `Result<T>`)
250+
- If a new type is needed, create it in its own file in the appropriate namespace
251+
- Check for existing types before creating duplicates
252+
253+
### SignalR Communication Patterns
254+
255+
- Device heartbeats flow: `Agent``AgentHub.UpdateDevice()``ViewerHub` groups
256+
- Remote control requests: `ViewerHub``AgentHub``IPC``DesktopClient`
257+
- Use hub groups for tenant isolation and role-based access control
258+
- Group naming pattern: `HubGroupNames.GetTenantDevicesGroupName()`, `GetTagGroupName()`, etc.
259+
260+
### Testing Strategy
261+
262+
- Use xUnit for unit testing.
263+
- Write unit tests for business logic and services.
264+
- Maintain test coverage for shared libraries.
265+
- For server tests, use helpers `Tests\ControlR.Web.Server.Tests\Helpers\` when appropriate.
266+
267+
### Security Considerations
268+
269+
- Always validate and sanitize user inputs.
270+
- Use `AuthorizeAttribute` and `IAuthorizationService` for endpoint authorization.
271+
272+
### Performance Guidelines
273+
274+
- Optimize database queries with proper indexing.
275+
- Use async/await patterns for I/O operations.
276+
- Cache frequently accessed data appropriately.
277+
278+
### Error Handling
279+
280+
- Use structured logging with Serilog.
281+
- Implement proper exception handling and recovery.
282+
- Provide meaningful error messages to users.
283+
- Log errors with appropriate context for debugging.
284+
285+
### Documentation
286+
287+
- Use XML documentation comments for public APIs.
288+
- Maintain README files for complex components.
289+
- Document configuration options and environment variables.
290+
- Keep API documentation up to date.

.github/workflows/build.yml

Lines changed: 16 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
name: Build
2-
run-name: Build v${{ vars.CURRENT_VERSION }}
2+
run-name: Build v${{ inputs.version || vars.CURRENT_VERSION }}
33

44
on:
55
#push:
@@ -21,8 +21,14 @@ on:
2121
default: 1
2222
type: number
2323

24+
version:
25+
description: "Version number (leave empty for auto)"
26+
required: false
27+
default: ""
28+
type: string
29+
2430
env:
25-
VERSION: ${{vars.CURRENT_VERSION}}
31+
VERSION: ${{ inputs.version || vars.CURRENT_VERSION }}
2632
RETENTION_DAYS_ARTIFACTS: ${{ inputs.retention_days_artifacts || 1 }}
2733

2834
jobs:
@@ -47,11 +53,16 @@ jobs:
4753
id: setver
4854
shell: pwsh
4955
run: |
50-
$CurrentVersion = [System.Version]::Parse("${{ vars.CURRENT_VERSION }}")
51-
echo "Resolved current version: $CurrentVersion"
56+
# Use supplied version if provided, otherwise fall back to the repo variable
57+
Write-Host "Using source version: $env:VERSION"
58+
$CurrentVersion = [System.Version]::Parse("$env:VERSION")
59+
Write-Host "Resolved current version: $CurrentVersion"
5260
$NextVersion = "$($CurrentVersion.Major).$($CurrentVersion.Minor).$($CurrentVersion.Build + 1).0"
53-
echo "Next version will be: $NextVersion"
61+
Write-Host "Next version will be: $NextVersion"
62+
# Persist next version to GH Variables for future runs
5463
gh variable set CURRENT_VERSION --body $NextVersion
64+
# Expose the next version as a step output for the job
65+
echo "version=$NextVersion" >> $GITHUB_OUTPUT
5566
5667
run-tests:
5768
name: Run Tests

0 commit comments

Comments
 (0)