mirror of
https://github.com/netbirdio/IronRDP.git
synced 2026-05-22 18:43:12 -07:00
refactor(web-client): introduce ConfigBuilder for a better connection API (#749)
This commit is contained in:
@@ -0,0 +1,28 @@
|
||||
type ExtensionValue = { Pcb: string } | { KdcProxyUrl: string } | { DisplayControl: boolean };
|
||||
|
||||
export class Extension {
|
||||
static init(ident: string, value: unknown): ExtensionValue {
|
||||
switch (ident) {
|
||||
case 'Pcb':
|
||||
if (typeof value === 'string') {
|
||||
return { Pcb: value };
|
||||
} else {
|
||||
throw new Error('Pcb must be a string');
|
||||
}
|
||||
case 'KdcProxyUrl':
|
||||
if (typeof value === 'string') {
|
||||
return { KdcProxyUrl: value };
|
||||
} else {
|
||||
throw new Error('KdcProxyUrl must be a string');
|
||||
}
|
||||
case 'DisplayControl':
|
||||
if (typeof value === 'boolean') {
|
||||
return { DisplayControl: value };
|
||||
} else {
|
||||
throw new Error('DisplayControl must be a boolean');
|
||||
}
|
||||
default:
|
||||
throw new Error(`Invalid extension type: ${ident}`);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -10,6 +10,7 @@ import init, {
|
||||
ClipboardTransaction,
|
||||
ClipboardContent,
|
||||
} from '../../../crates/ironrdp-web/pkg/ironrdp_web';
|
||||
import { Extension } from './interfaces/Extension';
|
||||
|
||||
export default {
|
||||
init,
|
||||
@@ -23,4 +24,5 @@ export default {
|
||||
ClipboardContent,
|
||||
Session,
|
||||
SessionTerminationInfo,
|
||||
Extension,
|
||||
};
|
||||
|
||||
@@ -0,0 +1,5 @@
|
||||
import type { ExtensionValue } from './ExtensionValue';
|
||||
|
||||
export interface Extension {
|
||||
init(ident: string, value: unknown): ExtensionValue;
|
||||
}
|
||||
@@ -0,0 +1 @@
|
||||
export type ExtensionValue = unknown;
|
||||
@@ -7,6 +7,7 @@ import type { SessionBuilder } from './SessionBuilder';
|
||||
import type { SessionTerminationInfo } from './SessionTerminationInfo';
|
||||
import type { ClipboardTransaction } from './ClipboardTransaction';
|
||||
import type { ClipboardContent } from './ClipboardContent';
|
||||
import type { Extension } from './Extension';
|
||||
|
||||
export interface RemoteDesktopModule {
|
||||
init: () => Promise<unknown>;
|
||||
@@ -20,4 +21,5 @@ export interface RemoteDesktopModule {
|
||||
SessionTerminationInfo: SessionTerminationInfo;
|
||||
ClipboardTransaction: ClipboardTransaction;
|
||||
ClipboardContent: ClipboardContent;
|
||||
Extension: Extension;
|
||||
}
|
||||
|
||||
@@ -1,25 +1,17 @@
|
||||
import type { ScreenScale } from '../enums/ScreenScale';
|
||||
import type { NewSessionInfo } from './NewSessionInfo';
|
||||
import type { SessionEvent } from './session-event';
|
||||
import type { DesktopSize } from './DesktopSize';
|
||||
import { ConfigBuilder } from '../services/ConfigBuilder';
|
||||
import type { Config } from '../services/Config';
|
||||
|
||||
export interface UserInteraction {
|
||||
setVisibility(state: boolean): void;
|
||||
|
||||
setScale(scale: ScreenScale): void;
|
||||
|
||||
connect(
|
||||
username: string,
|
||||
password: string,
|
||||
destination: string,
|
||||
proxyAddress: string,
|
||||
serverDomain: string,
|
||||
authToken: string,
|
||||
desktopSize?: DesktopSize,
|
||||
preConnectionBlob?: string,
|
||||
kdc_proxy_url?: string,
|
||||
use_display_control?: boolean,
|
||||
): Promise<NewSessionInfo>;
|
||||
configBuilder(): ConfigBuilder;
|
||||
|
||||
connect(config: Config): Promise<NewSessionInfo>;
|
||||
|
||||
setKeyboardUnicodeMode(use_unicode: boolean): void;
|
||||
|
||||
|
||||
@@ -0,0 +1,33 @@
|
||||
import type { DesktopSize } from '../interfaces/DesktopSize';
|
||||
import type { ExtensionValue } from '../interfaces/ExtensionValue';
|
||||
|
||||
export class Config {
|
||||
readonly username: string;
|
||||
readonly password: string;
|
||||
readonly destination: string;
|
||||
readonly proxyAddress: string;
|
||||
readonly serverDomain: string;
|
||||
readonly authToken: string;
|
||||
readonly desktopSize?: DesktopSize;
|
||||
readonly extensions: ExtensionValue[];
|
||||
|
||||
constructor(
|
||||
userData: { username: string; password: string },
|
||||
proxyData: { address: string; authToken: string },
|
||||
configOptions: {
|
||||
destination: string;
|
||||
serverDomain: string;
|
||||
extensions: ExtensionValue[];
|
||||
desktopSize?: DesktopSize;
|
||||
},
|
||||
) {
|
||||
this.username = userData.username;
|
||||
this.password = userData.password;
|
||||
this.proxyAddress = proxyData.address;
|
||||
this.authToken = proxyData.authToken;
|
||||
this.destination = configOptions.destination;
|
||||
this.serverDomain = configOptions.serverDomain;
|
||||
this.extensions = configOptions.extensions;
|
||||
this.desktopSize = configOptions.desktopSize;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,160 @@
|
||||
import type { DesktopSize } from '../interfaces/DesktopSize';
|
||||
import { Config } from './Config';
|
||||
import type { ExtensionValue } from '../interfaces/ExtensionValue';
|
||||
|
||||
type ExtensionConstructor = (ident: string, value: unknown) => ExtensionValue;
|
||||
|
||||
/**
|
||||
* Builder class for creating Config objects with a fluent interface.
|
||||
*
|
||||
* @example
|
||||
* ```typescript
|
||||
* const configBuilder = new ConfigBuilder(createExtensionFunction);
|
||||
* const config = configBuilder
|
||||
* .withDestination(destination)
|
||||
* .withProxyAddress(proxyAddress)
|
||||
* .withAuthToken(authToken)
|
||||
* ...
|
||||
* .build();
|
||||
* ```
|
||||
*/
|
||||
export class ConfigBuilder {
|
||||
private extensionConstructor: ExtensionConstructor;
|
||||
|
||||
private username: string = '';
|
||||
private password: string = '';
|
||||
private destination: string = '';
|
||||
private proxyAddress: string = '';
|
||||
private serverDomain: string = '';
|
||||
private authToken: string = '';
|
||||
private desktopSize?: DesktopSize;
|
||||
|
||||
private extensions: ExtensionValue[] = [];
|
||||
|
||||
/**
|
||||
* Creates a new ConfigBuilder instance.
|
||||
*
|
||||
* @param extensionConstructor - Function that creates extension values from identifiers and values.
|
||||
*/
|
||||
constructor(extensionConstructor: ExtensionConstructor) {
|
||||
this.extensionConstructor = extensionConstructor;
|
||||
}
|
||||
|
||||
/**
|
||||
* Optional parameter
|
||||
*
|
||||
* @param username - The username to use for authentication
|
||||
* @returns The builder instance for method chaining
|
||||
*/
|
||||
withUsername(username: string): ConfigBuilder {
|
||||
this.username = username;
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Optional parameter
|
||||
*
|
||||
* @param password - The password for authentication
|
||||
* @returns The builder instance for method chaining
|
||||
*/
|
||||
withPassword(password: string): ConfigBuilder {
|
||||
this.password = password;
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Required parameter
|
||||
*
|
||||
* @param destination - The destination address to connect to
|
||||
* @returns The builder instance for method chaining
|
||||
*/
|
||||
withDestination(destination: string): ConfigBuilder {
|
||||
this.destination = destination;
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Required parameter
|
||||
*
|
||||
* @param proxyAddress - The address of the proxy server
|
||||
* @returns The builder instance for method chaining
|
||||
*/
|
||||
withProxyAddress(proxyAddress: string): ConfigBuilder {
|
||||
this.proxyAddress = proxyAddress;
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Optional parameter
|
||||
*
|
||||
* @param serverDomain - The server domain to connect to
|
||||
* @returns The builder instance for method chaining
|
||||
*/
|
||||
withServerDomain(serverDomain: string): ConfigBuilder {
|
||||
this.serverDomain = serverDomain;
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Required parameter
|
||||
*
|
||||
* @param authToken - JWT token to connect to the proxy
|
||||
* @returns The builder instance for method chaining
|
||||
*/
|
||||
withAuthToken(authToken: string): ConfigBuilder {
|
||||
this.authToken = authToken;
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Optional parameter
|
||||
*
|
||||
* @param ident - The identifier for the extension
|
||||
* @param value - The value for the extension
|
||||
* @returns The builder instance for method chaining
|
||||
*/
|
||||
withExtension(ident: string, value: unknown): ConfigBuilder {
|
||||
this.extensions.push(this.extensionConstructor(ident, value));
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Optional
|
||||
*
|
||||
* @param desktopSize - The desktop size configuration object
|
||||
* @returns The builder instance for method chaining
|
||||
*/
|
||||
withDesktopSize(desktopSize: DesktopSize): ConfigBuilder {
|
||||
this.desktopSize = desktopSize;
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Builds a new Config instance.
|
||||
*
|
||||
* @throws {Error} If required parameters (destination, proxyAddress, authToken) are not set
|
||||
* @returns A new Config instance with the configured values
|
||||
*/
|
||||
build(): Config {
|
||||
if (this.destination === '') {
|
||||
throw new Error('destination has to be specified');
|
||||
}
|
||||
if (this.proxyAddress === '') {
|
||||
throw new Error('proxy address has to be specified');
|
||||
}
|
||||
if (this.authToken === '') {
|
||||
throw new Error('authentication token has to be specified');
|
||||
}
|
||||
const userData = { username: this.username, password: this.password };
|
||||
const proxyData = { address: this.proxyAddress, authToken: this.authToken };
|
||||
|
||||
const configOptions = {
|
||||
destination: this.destination,
|
||||
serverDomain: this.serverDomain,
|
||||
extensions: this.extensions,
|
||||
desktopSize: this.desktopSize,
|
||||
};
|
||||
|
||||
return new Config(userData, proxyData, configOptions);
|
||||
}
|
||||
}
|
||||
@@ -4,7 +4,8 @@ import { SpecialCombination } from '../enums/SpecialCombination';
|
||||
import { RemoteDesktopService } from './remote-desktop.service';
|
||||
import type { UserInteraction } from '../interfaces/UserInteraction';
|
||||
import type { ScreenScale } from '../enums/ScreenScale';
|
||||
import type { DesktopSize } from '../interfaces/DesktopSize';
|
||||
import { ConfigBuilder } from './ConfigBuilder';
|
||||
import { Config } from './Config';
|
||||
|
||||
export class PublicAPI {
|
||||
private remoteDesktopService: RemoteDesktopService;
|
||||
@@ -13,31 +14,13 @@ export class PublicAPI {
|
||||
this.remoteDesktopService = remoteDesktopService;
|
||||
}
|
||||
|
||||
private connect(
|
||||
username: string,
|
||||
password: string,
|
||||
destination: string,
|
||||
proxyAddress: string,
|
||||
serverDomain: string,
|
||||
authToken: string,
|
||||
desktopSize?: DesktopSize,
|
||||
preConnectionBlob?: string,
|
||||
kdc_proxy_url?: string,
|
||||
use_display_control = false,
|
||||
): Promise<NewSessionInfo> {
|
||||
private configBuilder(): ConfigBuilder {
|
||||
return this.remoteDesktopService.configBuilder();
|
||||
}
|
||||
|
||||
private connect(config: Config): Promise<NewSessionInfo> {
|
||||
loggingService.info('Initializing connection.');
|
||||
const resultObservable = this.remoteDesktopService.connect(
|
||||
username,
|
||||
password,
|
||||
destination,
|
||||
proxyAddress,
|
||||
serverDomain,
|
||||
authToken,
|
||||
desktopSize,
|
||||
preConnectionBlob,
|
||||
kdc_proxy_url,
|
||||
use_display_control,
|
||||
);
|
||||
const resultObservable = this.remoteDesktopService.connect(config);
|
||||
|
||||
return resultObservable.toPromise();
|
||||
}
|
||||
@@ -82,6 +65,7 @@ export class PublicAPI {
|
||||
getExposedFunctions(): UserInteraction {
|
||||
return {
|
||||
setVisibility: this.setVisibility.bind(this),
|
||||
configBuilder: this.configBuilder.bind(this),
|
||||
connect: this.connect.bind(this),
|
||||
setScale: this.setScale.bind(this),
|
||||
onSessionEvent: (callback) => {
|
||||
|
||||
@@ -13,13 +13,14 @@ import type { ResizeEvent } from '../interfaces/ResizeEvent';
|
||||
import { ScreenScale } from '../enums/ScreenScale';
|
||||
import type { MousePosition } from '../interfaces/MousePosition';
|
||||
import type { SessionEvent, IronErrorKind, IronError } from '../interfaces/session-event';
|
||||
import type { DesktopSize } from '../interfaces/DesktopSize';
|
||||
import type { ClipboardTransaction } from '../interfaces/ClipboardTransaction';
|
||||
import type { ClipboardContent } from '../interfaces/ClipboardContent';
|
||||
import type { Session } from '../interfaces/Session';
|
||||
import type { DeviceEvent } from '../interfaces/DeviceEvent';
|
||||
import type { SessionTerminationInfo } from '../interfaces/SessionTerminationInfo';
|
||||
import type { RemoteDesktopModule } from '../interfaces/RemoteDesktopModule';
|
||||
import { ConfigBuilder } from './ConfigBuilder';
|
||||
import type { Config } from './Config';
|
||||
|
||||
type OnRemoteClipboardChanged = (transaction: ClipboardTransaction) => void;
|
||||
type OnRemoteReceivedFormatsList = () => void;
|
||||
@@ -135,37 +136,27 @@ export class RemoteDesktopService {
|
||||
this.mousePosition.next(position);
|
||||
}
|
||||
|
||||
connect(
|
||||
username: string,
|
||||
password: string,
|
||||
destination: string,
|
||||
proxyAddress: string,
|
||||
serverDomain: string,
|
||||
authToken: string,
|
||||
desktopSize?: DesktopSize,
|
||||
preConnectionBlob?: string,
|
||||
kdc_proxy_url?: string,
|
||||
use_display_control = true,
|
||||
): Observable<NewSessionInfo> {
|
||||
configBuilder(): ConfigBuilder {
|
||||
return new ConfigBuilder(this.module.Extension.init);
|
||||
}
|
||||
|
||||
connect(config: Config): Observable<NewSessionInfo> {
|
||||
const sessionBuilder = this.module.SessionBuilder.init();
|
||||
|
||||
sessionBuilder.proxy_address(proxyAddress);
|
||||
sessionBuilder.destination(destination);
|
||||
sessionBuilder.server_domain(serverDomain);
|
||||
sessionBuilder.password(password);
|
||||
sessionBuilder.auth_token(authToken);
|
||||
sessionBuilder.username(username);
|
||||
sessionBuilder.proxy_address(config.proxyAddress);
|
||||
sessionBuilder.destination(config.destination);
|
||||
sessionBuilder.server_domain(config.serverDomain);
|
||||
sessionBuilder.password(config.password);
|
||||
sessionBuilder.auth_token(config.authToken);
|
||||
sessionBuilder.username(config.username);
|
||||
sessionBuilder.render_canvas(this.canvas!);
|
||||
sessionBuilder.set_cursor_style_callback_context(this);
|
||||
sessionBuilder.set_cursor_style_callback(this.setCursorStyleCallback);
|
||||
sessionBuilder.extension({ DisplayControl: use_display_control });
|
||||
|
||||
if (preConnectionBlob != null) {
|
||||
sessionBuilder.extension({ Pcb: preConnectionBlob });
|
||||
}
|
||||
if (kdc_proxy_url != null) {
|
||||
sessionBuilder.extension({ KdcProxyUrl: kdc_proxy_url });
|
||||
}
|
||||
config.extensions.forEach((extension) => {
|
||||
sessionBuilder.extension(extension);
|
||||
});
|
||||
|
||||
if (this.onRemoteClipboardChanged != null && this.enableClipboard) {
|
||||
sessionBuilder.remote_clipboard_changed_callback(this.onRemoteClipboardChanged);
|
||||
}
|
||||
@@ -176,8 +167,10 @@ export class RemoteDesktopService {
|
||||
sessionBuilder.force_clipboard_update_callback(this.onForceClipboardUpdate);
|
||||
}
|
||||
|
||||
if (desktopSize != null) {
|
||||
sessionBuilder.desktop_size(this.module.DesktopSize.init(desktopSize.width, desktopSize.height));
|
||||
if (config.desktopSize != null) {
|
||||
sessionBuilder.desktop_size(
|
||||
this.module.DesktopSize.init(config.desktopSize.width, config.desktopSize.height),
|
||||
);
|
||||
}
|
||||
|
||||
// Type guard to filter out errors
|
||||
|
||||
@@ -15,7 +15,7 @@
|
||||
let authtoken = '';
|
||||
let kdc_proxy_url = '';
|
||||
let desktopSize = new DesktopSize(1280, 768);
|
||||
let pcb: string;
|
||||
let pcb = '';
|
||||
let pop_up = false;
|
||||
let enable_clipboard = true;
|
||||
|
||||
@@ -115,20 +115,28 @@
|
||||
}
|
||||
|
||||
userInteraction.setEnableClipboard(enable_clipboard);
|
||||
from(
|
||||
userInteraction.connect(
|
||||
username,
|
||||
password,
|
||||
hostname,
|
||||
gatewayAddress,
|
||||
domain,
|
||||
authtoken,
|
||||
desktopSize,
|
||||
pcb,
|
||||
kdc_proxy_url,
|
||||
true,
|
||||
),
|
||||
)
|
||||
|
||||
const configBuilder = userInteraction
|
||||
.configBuilder()
|
||||
.withUsername(username)
|
||||
.withPassword(password)
|
||||
.withDestination(hostname)
|
||||
.withProxyAddress(gatewayAddress)
|
||||
.withServerDomain(domain)
|
||||
.withAuthToken(authtoken)
|
||||
.withDesktopSize(desktopSize)
|
||||
.withExtension('DisplayControl', true);
|
||||
|
||||
if (pcb !== '') {
|
||||
configBuilder.withExtension('Pcb', pcb);
|
||||
}
|
||||
|
||||
if (kdc_proxy_url !== '') {
|
||||
configBuilder.withExtension('KdcProxyUrl', kdc_proxy_url);
|
||||
}
|
||||
const config = configBuilder.build();
|
||||
|
||||
from(userInteraction.connect(config))
|
||||
.pipe(
|
||||
catchError((err) => {
|
||||
toast.set({
|
||||
|
||||
@@ -35,23 +35,31 @@
|
||||
const parsedData = JSON.parse(atob(data));
|
||||
const { hostname, gatewayAddress, domain, username, password, authtoken, kdc_proxy_url, pcb, desktopSize } =
|
||||
parsedData;
|
||||
uiService
|
||||
.connect(
|
||||
username,
|
||||
password,
|
||||
hostname,
|
||||
gatewayAddress,
|
||||
domain,
|
||||
authtoken,
|
||||
desktopSize,
|
||||
pcb,
|
||||
kdc_proxy_url,
|
||||
true,
|
||||
)
|
||||
.then(() => {
|
||||
uiService.setVisibility(true);
|
||||
window.onresize = onWindowResize;
|
||||
});
|
||||
|
||||
const configBuilder = uiService
|
||||
.configBuilder()
|
||||
.withUsername(username)
|
||||
.withPassword(password)
|
||||
.withDestination(hostname)
|
||||
.withProxyAddress(gatewayAddress)
|
||||
.withServerDomain(domain)
|
||||
.withAuthToken(authtoken)
|
||||
.withDesktopSize(desktopSize)
|
||||
.withExtension('DisplayControl', true);
|
||||
|
||||
if (pcb !== '') {
|
||||
configBuilder.withExtension('Pcb', pcb);
|
||||
}
|
||||
|
||||
if (kdc_proxy_url !== '') {
|
||||
configBuilder.withExtension('KdcProxyUrl', kdc_proxy_url);
|
||||
}
|
||||
const config = configBuilder.build();
|
||||
|
||||
uiService.connect(config).then(() => {
|
||||
uiService.setVisibility(true);
|
||||
window.onresize = onWindowResize;
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
|
||||
Reference in New Issue
Block a user