mirror of
https://github.com/Devolutions/IronRDP.git
synced 2025-12-23 12:26:46 +00:00
fix(web)!: rework error handling (#975)
Improves the error handling in _iron-remote-desktop_ by replacing the session events with throwing errors for terminated and error events and callbacks for warnings and the clipboard remote update event.
This commit is contained in:
parent
3182a018e2
commit
6c0014d5b3
11 changed files with 167 additions and 253 deletions
|
|
@ -1,9 +0,0 @@
|
|||
export enum SessionEventType {
|
||||
STARTED,
|
||||
TERMINATED,
|
||||
ERROR,
|
||||
WARNING,
|
||||
|
||||
// Clipboard events
|
||||
CLIPBOARD_REMOTE_UPDATE,
|
||||
}
|
||||
|
|
@ -1,6 +1,4 @@
|
|||
import type { SessionEventType } from '../enums/SessionEventType';
|
||||
|
||||
export enum IronErrorKind {
|
||||
export enum IronErrorKind {
|
||||
General = 0,
|
||||
WrongPassword = 1,
|
||||
LogonFailure = 2,
|
||||
|
|
@ -14,8 +12,3 @@ export interface IronError {
|
|||
backtrace: () => string;
|
||||
kind: () => IronErrorKind;
|
||||
}
|
||||
|
||||
export interface SessionEvent {
|
||||
type: SessionEventType;
|
||||
data: IronError | string;
|
||||
}
|
||||
|
|
@ -1,7 +1,9 @@
|
|||
import type { DesktopSize } from './DesktopSize';
|
||||
import type { SessionTerminationInfo } from './SessionTerminationInfo';
|
||||
|
||||
export interface NewSessionInfo {
|
||||
sessionId: number;
|
||||
websocketPort: number;
|
||||
initialDesktopSize: DesktopSize;
|
||||
run: () => Promise<SessionTerminationInfo>;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,6 +1,5 @@
|
|||
import type { ScreenScale } from '../enums/ScreenScale';
|
||||
import type { NewSessionInfo } from './NewSessionInfo';
|
||||
import type { SessionEvent } from './session-event';
|
||||
import { ConfigBuilder } from '../services/ConfigBuilder';
|
||||
import type { Config } from '../services/Config';
|
||||
import type { Extension } from './Extension';
|
||||
|
|
@ -25,7 +24,9 @@ export interface UserInteraction {
|
|||
|
||||
setCursorStyleOverride(style: string | null): void;
|
||||
|
||||
onSessionEvent(callback: Callback<SessionEvent>): void;
|
||||
onWarningCallback(callback: Callback<string>): void;
|
||||
|
||||
onClipboardRemoteUpdateCallback(callback: Callback<void>): void;
|
||||
|
||||
resize(width: number, height: number, scale?: number): void;
|
||||
|
||||
|
|
@ -33,9 +34,9 @@ export interface UserInteraction {
|
|||
|
||||
setEnableAutoClipboard(enable: boolean): void;
|
||||
|
||||
saveRemoteClipboardData(): Promise<boolean>;
|
||||
saveRemoteClipboardData(): Promise<void>;
|
||||
|
||||
sendClipboardData(): Promise<boolean>;
|
||||
sendClipboardData(): Promise<void>;
|
||||
|
||||
invokeExtension(ext: Extension): void;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,8 +1,7 @@
|
|||
export * as default from './iron-remote-desktop.svelte';
|
||||
export type { ResizeEvent } from './interfaces/ResizeEvent';
|
||||
export type { NewSessionInfo } from './interfaces/NewSessionInfo';
|
||||
export type { SessionEvent, IronError, IronErrorKind } from './interfaces/session-event';
|
||||
export type { SessionEventType } from './enums/SessionEventType';
|
||||
export type { IronError, IronErrorKind } from './interfaces/Error';
|
||||
export type { SessionTerminationInfo } from './interfaces/SessionTerminationInfo';
|
||||
export type { ClipboardData } from './interfaces/ClipboardData';
|
||||
export type { ClipboardItem } from './interfaces/ClipboardItem';
|
||||
|
|
|
|||
|
|
@ -68,11 +68,19 @@ export class PublicAPI {
|
|||
this.remoteDesktopService.setEnableAutoClipboard(enable);
|
||||
}
|
||||
|
||||
private async saveRemoteClipboardData(): Promise<boolean> {
|
||||
private setOnWarningCallback(callback: (data: string) => void) {
|
||||
this.remoteDesktopService.setOnWarningCallback(callback);
|
||||
}
|
||||
|
||||
private setOnClipboardRemoteUpdateCallback(callback: () => void) {
|
||||
this.remoteDesktopService.setOnClipboardRemoteUpdate(callback);
|
||||
}
|
||||
|
||||
private async saveRemoteClipboardData(): Promise<void> {
|
||||
return await this.clipboardService.saveRemoteClipboardData();
|
||||
}
|
||||
|
||||
private async sendClipboardData(): Promise<boolean> {
|
||||
private async sendClipboardData(): Promise<void> {
|
||||
return await this.clipboardService.sendClipboardData();
|
||||
}
|
||||
|
||||
|
|
@ -85,10 +93,9 @@ export class PublicAPI {
|
|||
setVisibility: this.setVisibility.bind(this),
|
||||
configBuilder: this.configBuilder.bind(this),
|
||||
connect: this.connect.bind(this),
|
||||
onWarningCallback: this.setOnWarningCallback.bind(this),
|
||||
onClipboardRemoteUpdateCallback: this.setOnClipboardRemoteUpdateCallback.bind(this),
|
||||
setScale: this.setScale.bind(this),
|
||||
onSessionEvent: (callback) => {
|
||||
this.remoteDesktopService.sessionEventObservable.subscribe(callback);
|
||||
},
|
||||
ctrlAltDel: this.ctrlAltDel.bind(this),
|
||||
metaKey: this.metaKey.bind(this),
|
||||
shutdown: this.shutdown.bind(this),
|
||||
|
|
|
|||
|
|
@ -4,11 +4,19 @@ import { get } from 'svelte/store';
|
|||
import type { ClipboardData } from '../interfaces/ClipboardData';
|
||||
import type { RemoteDesktopModule } from '../interfaces/RemoteDesktopModule';
|
||||
import { runWhenFocusedQueue } from '../lib/stores/runWhenFocusedStore';
|
||||
import { SessionEventType } from '../enums/SessionEventType';
|
||||
import { ClipboardApiSupported } from '../enums/ClipboardApiSupported';
|
||||
import { IronErrorKind } from '../interfaces/Error';
|
||||
|
||||
const CLIPBOARD_MONITORING_INTERVAL_MS = 100;
|
||||
|
||||
// Helper function to conveniently throw an `IronError`.
|
||||
function throwIronError(message: string): never {
|
||||
throw {
|
||||
kind: () => IronErrorKind.General,
|
||||
backtrace: () => message,
|
||||
};
|
||||
}
|
||||
|
||||
export class ClipboardService {
|
||||
private remoteDesktopService: RemoteDesktopService;
|
||||
private module: RemoteDesktopModule;
|
||||
|
|
@ -29,10 +37,7 @@ export class ClipboardService {
|
|||
initClipboard() {
|
||||
// Clipboard API is available only in secure contexts (HTTPS).
|
||||
if (!window.isSecureContext) {
|
||||
this.remoteDesktopService.raiseSessionEvent({
|
||||
type: SessionEventType.WARNING,
|
||||
data: 'Clipboard is available only in secure contexts (HTTPS).',
|
||||
});
|
||||
this.remoteDesktopService.emitWarningEvent('Clipboard is available only in secure contexts (HTTPS).');
|
||||
return;
|
||||
}
|
||||
|
||||
|
|
@ -42,26 +47,23 @@ export class ClipboardService {
|
|||
this.ClipboardApiSupported = ClipboardApiSupported.Full;
|
||||
} else if (navigator.clipboard.readText != undefined) {
|
||||
this.ClipboardApiSupported = ClipboardApiSupported.TextOnly;
|
||||
this.remoteDesktopService.raiseSessionEvent({
|
||||
type: SessionEventType.WARNING,
|
||||
data: 'Clipboard is limited to text-only data types due to an outdated browser version!',
|
||||
});
|
||||
this.remoteDesktopService.emitWarningEvent(
|
||||
'Clipboard is limited to text-only data types due to an outdated browser version!',
|
||||
);
|
||||
} else if (navigator.clipboard.writeText != undefined) {
|
||||
this.ClipboardApiSupported = ClipboardApiSupported.TextOnlyServerOnly;
|
||||
this.remoteDesktopService.raiseSessionEvent({
|
||||
type: SessionEventType.WARNING,
|
||||
data: 'Clipboard reading is not supported and writing is limited to text-only data types due to an outdated browser version!',
|
||||
});
|
||||
this.remoteDesktopService.emitWarningEvent(
|
||||
'Clipboard reading is not supported and writing is limited to text-only data types due to an outdated browser version!',
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
// The basic Clipboard API is widely supported in modern browsers,
|
||||
// so this condition should never be true in practice.
|
||||
if (this.ClipboardApiSupported === ClipboardApiSupported.None) {
|
||||
this.remoteDesktopService.raiseSessionEvent({
|
||||
type: SessionEventType.WARNING,
|
||||
data: 'Clipboard is not supported due to an outdated browser version!',
|
||||
});
|
||||
this.remoteDesktopService.emitWarningEvent(
|
||||
'Clipboard is not supported due to an outdated browser version!',
|
||||
);
|
||||
return;
|
||||
}
|
||||
|
||||
|
|
@ -84,17 +86,13 @@ export class ClipboardService {
|
|||
|
||||
// Copies clipboard content received from the server to the local clipboard.
|
||||
// Returns the result of the operation. On failure, it additionally raises an error session event.
|
||||
async saveRemoteClipboardData(): Promise<boolean> {
|
||||
async saveRemoteClipboardData(): Promise<void> {
|
||||
if (this.ClipboardApiSupported !== ClipboardApiSupported.Full) {
|
||||
return await this.ffSaveRemoteClipboardData();
|
||||
}
|
||||
|
||||
if (this.clipboardDataToSave == null) {
|
||||
this.remoteDesktopService.raiseSessionEvent({
|
||||
type: SessionEventType.ERROR,
|
||||
data: 'The server did not send the clipboard data.',
|
||||
});
|
||||
return false;
|
||||
throwIronError('The server did not send the clipboard data.');
|
||||
}
|
||||
|
||||
try {
|
||||
|
|
@ -103,74 +101,53 @@ export class ClipboardService {
|
|||
await navigator.clipboard.write([clipboard_item]);
|
||||
|
||||
this.clipboardDataToSave = null;
|
||||
return true;
|
||||
} catch (err) {
|
||||
this.remoteDesktopService.raiseSessionEvent({
|
||||
type: SessionEventType.ERROR,
|
||||
data: 'Failed to write to the clipboard: ' + err,
|
||||
});
|
||||
return false;
|
||||
throwIronError('Failed to write to the clipboard: ' + err);
|
||||
}
|
||||
}
|
||||
|
||||
// Sends local clipboard's content to the server.
|
||||
// Returns the result of the operation. On failure, it additionally raises an error session event.
|
||||
async sendClipboardData(): Promise<boolean> {
|
||||
async sendClipboardData(): Promise<void> {
|
||||
if (this.ClipboardApiSupported !== ClipboardApiSupported.Full) {
|
||||
return await this.ffSendClipboardData();
|
||||
}
|
||||
|
||||
try {
|
||||
const value = await navigator.clipboard.read();
|
||||
const value = await navigator.clipboard.read().catch((err) => {
|
||||
throwIronError('Failed to read from the clipboard: ' + err);
|
||||
});
|
||||
|
||||
// Clipboard is empty
|
||||
if (value.length == 0) {
|
||||
this.remoteDesktopService.raiseSessionEvent({
|
||||
type: SessionEventType.ERROR,
|
||||
data: 'The clipboard has no data.',
|
||||
});
|
||||
return false;
|
||||
// Clipboard is empty
|
||||
if (value.length == 0) {
|
||||
throwIronError('The clipboard has no data.');
|
||||
}
|
||||
|
||||
// We only support one item at a time
|
||||
const item = value[0];
|
||||
|
||||
if (!item.types.some((type) => type.startsWith('text/') || type.startsWith('image/png'))) {
|
||||
// Unsupported types
|
||||
throwIronError('The clipboard has no data of supported type (text or image).');
|
||||
}
|
||||
|
||||
const clipboardData = new this.module.ClipboardData();
|
||||
|
||||
for (const kind of item.types) {
|
||||
// Get blob
|
||||
const blobIsString = kind.startsWith('text/');
|
||||
const blob = await item.getType(kind);
|
||||
|
||||
if (blobIsString) {
|
||||
clipboardData.addText(kind, await blob.text());
|
||||
} else {
|
||||
clipboardData.addBinary(kind, new Uint8Array(await blob.arrayBuffer()));
|
||||
}
|
||||
}
|
||||
|
||||
// We only support one item at a time
|
||||
const item = value[0];
|
||||
|
||||
if (!item.types.some((type) => type.startsWith('text/') || type.startsWith('image/png'))) {
|
||||
// Unsupported types
|
||||
this.remoteDesktopService.raiseSessionEvent({
|
||||
type: SessionEventType.ERROR,
|
||||
data: 'The clipboard has no data of supported type (text or image).',
|
||||
});
|
||||
return false;
|
||||
}
|
||||
|
||||
const clipboardData = new this.module.ClipboardData();
|
||||
|
||||
for (const kind of item.types) {
|
||||
// Get blob
|
||||
const blobIsString = kind.startsWith('text/');
|
||||
const blob = await item.getType(kind);
|
||||
|
||||
if (blobIsString) {
|
||||
clipboardData.addText(kind, await blob.text());
|
||||
} else {
|
||||
clipboardData.addBinary(kind, new Uint8Array(await blob.arrayBuffer()));
|
||||
}
|
||||
}
|
||||
|
||||
if (!clipboardData.isEmpty()) {
|
||||
this.lastSentClipboardData = clipboardData;
|
||||
// TODO(Fix): onClipboardChanged takes an ownership over clipboardData, so lastSentClipboardData will be nullptr.
|
||||
await this.remoteDesktopService.onClipboardChanged(clipboardData);
|
||||
}
|
||||
|
||||
return true;
|
||||
} catch (err) {
|
||||
this.remoteDesktopService.raiseSessionEvent({
|
||||
type: SessionEventType.ERROR,
|
||||
data: 'Failed to read from the clipboard: ' + err,
|
||||
});
|
||||
return false;
|
||||
if (!clipboardData.isEmpty()) {
|
||||
this.lastSentClipboardData = clipboardData;
|
||||
// TODO(Fix): onClipboardChanged takes an ownership over clipboardData, so lastSentClipboardData will be nullptr.
|
||||
await this.remoteDesktopService.onClipboardChanged(clipboardData);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -223,10 +200,7 @@ export class ClipboardService {
|
|||
// This callback is required to update client clipboard state when remote side has changed.
|
||||
private onRemoteClipboardChangedManualMode(data: ClipboardData) {
|
||||
this.clipboardDataToSave = data;
|
||||
this.remoteDesktopService.raiseSessionEvent({
|
||||
type: SessionEventType.CLIPBOARD_REMOTE_UPDATE,
|
||||
data: '',
|
||||
});
|
||||
this.remoteDesktopService.emitClipboardRemoteUpdateEvent();
|
||||
}
|
||||
|
||||
// This callback is required to update client clipboard state when remote side has changed.
|
||||
|
|
@ -244,7 +218,7 @@ export class ClipboardService {
|
|||
}
|
||||
|
||||
// Called periodically to monitor clipboard changes
|
||||
private async onMonitorClipboard() {
|
||||
private async onMonitorClipboard(): Promise<void> {
|
||||
try {
|
||||
if (!document.hasFocus()) {
|
||||
return;
|
||||
|
|
@ -382,35 +356,23 @@ export class ClipboardService {
|
|||
if (value === '') return;
|
||||
|
||||
this.ffClipboardDataToSave = value;
|
||||
this.remoteDesktopService.raiseSessionEvent({
|
||||
type: SessionEventType.CLIPBOARD_REMOTE_UPDATE,
|
||||
data: '',
|
||||
});
|
||||
this.remoteDesktopService.emitClipboardRemoteUpdateEvent();
|
||||
}
|
||||
|
||||
// Firefox specific function. We are using text-only clipboard API here.
|
||||
//
|
||||
// Copies clipboard content received from the server to the local clipboard.
|
||||
// Returns the result of the operation. On failure, it additionally raises an error session event.
|
||||
private async ffSaveRemoteClipboardData(): Promise<boolean> {
|
||||
private async ffSaveRemoteClipboardData(): Promise<void> {
|
||||
if (this.ffClipboardDataToSave == null) {
|
||||
this.remoteDesktopService.raiseSessionEvent({
|
||||
type: SessionEventType.ERROR,
|
||||
data: 'The server did not send the clipboard data.',
|
||||
});
|
||||
return false;
|
||||
throwIronError('The server did not send the clipboard data.');
|
||||
}
|
||||
|
||||
try {
|
||||
await navigator.clipboard.writeText(this.ffClipboardDataToSave);
|
||||
this.ffClipboardDataToSave = null;
|
||||
return true;
|
||||
} catch (err) {
|
||||
this.remoteDesktopService.raiseSessionEvent({
|
||||
type: SessionEventType.ERROR,
|
||||
data: 'Failed to write to the clipboard: ' + err,
|
||||
});
|
||||
return false;
|
||||
throwIronError('Failed to write to the clipboard: ' + err);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -418,43 +380,27 @@ export class ClipboardService {
|
|||
//
|
||||
// Sends local clipboard's content to the server.
|
||||
// Returns the result of the operation. On failure, it additionally raises an error session event.
|
||||
private async ffSendClipboardData(): Promise<boolean> {
|
||||
private async ffSendClipboardData(): Promise<void> {
|
||||
if (this.ClipboardApiSupported !== ClipboardApiSupported.TextOnly) {
|
||||
this.remoteDesktopService.raiseSessionEvent({
|
||||
type: SessionEventType.ERROR,
|
||||
data: 'The browser does not support clipboard read.',
|
||||
});
|
||||
return false;
|
||||
throwIronError('The browser does not support clipboard read.');
|
||||
}
|
||||
|
||||
try {
|
||||
const value = await navigator.clipboard.readText();
|
||||
const value = await navigator.clipboard.readText().catch((err) => {
|
||||
throwIronError('Failed to read from the clipboard: ' + err);
|
||||
});
|
||||
|
||||
// Clipboard is empty
|
||||
if (value.length == 0) {
|
||||
this.remoteDesktopService.raiseSessionEvent({
|
||||
type: SessionEventType.ERROR,
|
||||
data: 'The clipboard has no data.',
|
||||
});
|
||||
return false;
|
||||
}
|
||||
// Clipboard is empty
|
||||
if (value.length == 0) {
|
||||
throwIronError('The clipboard has no data.');
|
||||
}
|
||||
|
||||
const clipboardData = new this.module.ClipboardData();
|
||||
clipboardData.addText('text/plain', value);
|
||||
const clipboardData = new this.module.ClipboardData();
|
||||
clipboardData.addText('text/plain', value);
|
||||
|
||||
if (!clipboardData.isEmpty()) {
|
||||
this.lastSentClipboardData = clipboardData;
|
||||
// TODO(Fix): onClipboardChanged takes an ownership over clipboardData, so lastSentClipboardData will be nullptr.
|
||||
await this.remoteDesktopService.onClipboardChanged(clipboardData);
|
||||
}
|
||||
|
||||
return true;
|
||||
} catch (err) {
|
||||
this.remoteDesktopService.raiseSessionEvent({
|
||||
type: SessionEventType.ERROR,
|
||||
data: 'Failed to read from the clipboard: ' + err,
|
||||
});
|
||||
return false;
|
||||
if (!clipboardData.isEmpty()) {
|
||||
this.lastSentClipboardData = clipboardData;
|
||||
// TODO(Fix): onClipboardChanged takes an ownership over clipboardData, so lastSentClipboardData will be nullptr.
|
||||
await this.remoteDesktopService.onClipboardChanged(clipboardData);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -2,13 +2,11 @@ import { loggingService } from './logging.service';
|
|||
import { scanCode } from '../lib/scancodes';
|
||||
import { ModifierKey } from '../enums/ModifierKey';
|
||||
import { LockKey } from '../enums/LockKey';
|
||||
import { SessionEventType } from '../enums/SessionEventType';
|
||||
import type { NewSessionInfo } from '../interfaces/NewSessionInfo';
|
||||
import { SpecialCombination } from '../enums/SpecialCombination';
|
||||
import type { ResizeEvent } from '../interfaces/ResizeEvent';
|
||||
import { ScreenScale } from '../enums/ScreenScale';
|
||||
import type { MousePosition } from '../interfaces/MousePosition';
|
||||
import type { IronError, IronErrorKind, SessionEvent } from '../interfaces/session-event';
|
||||
import type { ClipboardData } from '../interfaces/ClipboardData';
|
||||
import type { Session } from '../interfaces/Session';
|
||||
import { RotationUnit } from '../interfaces/DeviceEvent';
|
||||
|
|
@ -24,6 +22,8 @@ type OnRemoteClipboardChanged = (data: ClipboardData) => void;
|
|||
type OnRemoteReceivedFormatsList = () => void;
|
||||
type OnForceClipboardUpdate = () => void;
|
||||
type OnCanvasResized = () => void;
|
||||
type OnWarning = (data: string) => void;
|
||||
type OnClipboardRemoteUpdate = () => void;
|
||||
|
||||
export class RemoteDesktopService {
|
||||
private module: RemoteDesktopModule;
|
||||
|
|
@ -34,6 +34,8 @@ export class RemoteDesktopService {
|
|||
private onRemoteReceivedFormatList?: OnRemoteReceivedFormatsList;
|
||||
private onForceClipboardUpdate?: OnForceClipboardUpdate;
|
||||
private onCanvasResized?: OnCanvasResized;
|
||||
private onWarningCallback?: OnWarning;
|
||||
private onClipboardRemoteUpdate?: OnClipboardRemoteUpdate;
|
||||
private cursorHasOverride: boolean = false;
|
||||
private lastCursorStyle: string = 'default';
|
||||
private enableClipboard: boolean = true;
|
||||
|
|
@ -46,7 +48,6 @@ export class RemoteDesktopService {
|
|||
|
||||
mousePositionObservable: Observable<MousePosition> = new Observable();
|
||||
changeVisibilityObservable: Observable<boolean> = new Observable();
|
||||
sessionEventObservable: Observable<SessionEvent> = new Observable();
|
||||
scaleObservable: Observable<ScreenScale> = new Observable();
|
||||
|
||||
dynamicResizeObservable: Observable<{ width: number; height: number }> = new Observable();
|
||||
|
|
@ -89,6 +90,16 @@ export class RemoteDesktopService {
|
|||
this.onCanvasResized = callback;
|
||||
}
|
||||
|
||||
/// Callback which is called when the warning event is emitted.
|
||||
setOnWarningCallback(callback: OnWarning) {
|
||||
this.onWarningCallback = callback;
|
||||
}
|
||||
|
||||
/// Callback which is called when the clipboard remote update event is emitted.
|
||||
setOnClipboardRemoteUpdate(callback: OnClipboardRemoteUpdate) {
|
||||
this.onClipboardRemoteUpdate = callback;
|
||||
}
|
||||
|
||||
mouseIn(event: MouseEvent) {
|
||||
this.syncModifier(event);
|
||||
}
|
||||
|
|
@ -160,21 +171,7 @@ export class RemoteDesktopService {
|
|||
);
|
||||
}
|
||||
|
||||
const session = await sessionBuilder.connect().catch((err: IronError) => {
|
||||
this.raiseSessionEvent({
|
||||
type: SessionEventType.TERMINATED,
|
||||
data: {
|
||||
backtrace: () => err.backtrace(),
|
||||
kind: () => err.kind() as number as IronErrorKind,
|
||||
},
|
||||
});
|
||||
// The client must ignore this error and use session events for error handling.
|
||||
throw new Error();
|
||||
});
|
||||
|
||||
this.run(session);
|
||||
|
||||
loggingService.info('Session started.');
|
||||
const session = await sessionBuilder.connect();
|
||||
|
||||
this.session = session;
|
||||
|
||||
|
|
@ -182,38 +179,24 @@ export class RemoteDesktopService {
|
|||
desktopSize: session.desktopSize(),
|
||||
sessionId: 0,
|
||||
});
|
||||
this.raiseSessionEvent({
|
||||
type: SessionEventType.STARTED,
|
||||
data: 'Session started',
|
||||
});
|
||||
|
||||
const run = async (): Promise<SessionTerminationInfo> => {
|
||||
try {
|
||||
loggingService.info('Starting the session.');
|
||||
return await session.run();
|
||||
} finally {
|
||||
this.setVisibility(false);
|
||||
}
|
||||
};
|
||||
|
||||
return {
|
||||
sessionId: 0,
|
||||
initialDesktopSize: session.desktopSize(),
|
||||
websocketPort: 0,
|
||||
run,
|
||||
};
|
||||
}
|
||||
|
||||
run(session: Session) {
|
||||
session
|
||||
.run()
|
||||
.then((terminationInfo: SessionTerminationInfo) => {
|
||||
this.setVisibility(false);
|
||||
this.raiseSessionEvent({
|
||||
type: SessionEventType.TERMINATED,
|
||||
data: 'Session was terminated: ' + terminationInfo.reason() + '.',
|
||||
});
|
||||
})
|
||||
.catch((err: IronError) => {
|
||||
this.setVisibility(false);
|
||||
|
||||
this.raiseSessionEvent({
|
||||
type: SessionEventType.TERMINATED,
|
||||
data: 'Session was terminated with an error: ' + err.backtrace() + '.',
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
sendSpecialCombination(specialCombination: SpecialCombination): void {
|
||||
switch (specialCombination) {
|
||||
case SpecialCombination.CTRL_ALT_DEL:
|
||||
|
|
@ -248,6 +231,14 @@ export class RemoteDesktopService {
|
|||
]);
|
||||
}
|
||||
|
||||
emitWarningEvent(data: string): void {
|
||||
this.onWarningCallback?.(data);
|
||||
}
|
||||
|
||||
emitClipboardRemoteUpdateEvent(): void {
|
||||
this.onClipboardRemoteUpdate?.();
|
||||
}
|
||||
|
||||
setVisibility(state: boolean) {
|
||||
this.changeVisibilityObservable.publish(state);
|
||||
}
|
||||
|
|
@ -299,10 +290,6 @@ export class RemoteDesktopService {
|
|||
this.session?.invokeExtension(ext);
|
||||
}
|
||||
|
||||
raiseSessionEvent(event: SessionEvent) {
|
||||
this.sessionEventObservable.publish(event);
|
||||
}
|
||||
|
||||
private releaseAllInputs() {
|
||||
this.session?.releaseAllInputs();
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,6 +1,6 @@
|
|||
<script lang="ts">
|
||||
import { currentSession, userInteractionService } from '../../services/session.service';
|
||||
import type { UserInteraction } from '../../../static/iron-remote-desktop';
|
||||
import { currentSession, setCurrentSessionActive, userInteractionService } from '../../services/session.service';
|
||||
import type { IronError, UserInteraction } from '../../../static/iron-remote-desktop';
|
||||
import type { Session } from '../../models/session';
|
||||
import { preConnectionBlob, displayControl, kdcProxyUrl, init } from '../../../static/iron-remote-desktop-rdp';
|
||||
import { toast } from '$lib/messages/message-store';
|
||||
|
|
@ -21,31 +21,19 @@
|
|||
|
||||
let userInteraction: UserInteraction;
|
||||
|
||||
const initListeners = () => {
|
||||
userInteraction.onSessionEvent((event) => {
|
||||
if (event.type === 2) {
|
||||
console.log('Error event', event.data);
|
||||
|
||||
toast.set({
|
||||
type: 'error',
|
||||
message: typeof event.data !== 'string' ? event.data.backtrace() : event.data,
|
||||
});
|
||||
} else {
|
||||
toast.set({
|
||||
type: 'info',
|
||||
message: typeof event.data === 'string' ? event.data : event.data?.backtrace() ?? 'No info',
|
||||
});
|
||||
}
|
||||
});
|
||||
};
|
||||
|
||||
userInteractionService.subscribe((val) => {
|
||||
userInteraction = val;
|
||||
if (val != null) {
|
||||
initListeners();
|
||||
}
|
||||
});
|
||||
|
||||
const isIronError = (error: unknown): error is IronError => {
|
||||
return (
|
||||
typeof error === 'object' &&
|
||||
error !== null &&
|
||||
typeof (error as IronError).backtrace === 'function' &&
|
||||
typeof (error as IronError).kind === 'function'
|
||||
);
|
||||
};
|
||||
|
||||
const StartSession = async () => {
|
||||
if (authtoken === '') {
|
||||
const token_server_url = import.meta.env.VITE_IRON_TOKEN_SERVER_URL as string | undefined;
|
||||
|
|
@ -155,8 +143,30 @@
|
|||
currentSession.update(updater);
|
||||
|
||||
showLogin.set(false);
|
||||
|
||||
userInteraction.setVisibility(true);
|
||||
|
||||
const sessionTerminationInfo = await session_info.run();
|
||||
|
||||
toast.set({
|
||||
type: 'info',
|
||||
message: `Session terminated gracefully: ${sessionTerminationInfo.reason()}`,
|
||||
});
|
||||
} catch (err) {
|
||||
console.error(`Error occurred: ${err}`);
|
||||
setCurrentSessionActive(false);
|
||||
showLogin.set(true);
|
||||
|
||||
if (isIronError(err)) {
|
||||
toast.set({
|
||||
type: 'error',
|
||||
message: err.backtrace(),
|
||||
});
|
||||
} else {
|
||||
toast.set({
|
||||
type: 'error',
|
||||
message: `${err}`,
|
||||
});
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
|
|
|
|||
|
|
@ -1,7 +1,7 @@
|
|||
<script lang="ts">
|
||||
import { onMount } from 'svelte';
|
||||
import { setCurrentSessionActive, userInteractionService } from '../../services/session.service';
|
||||
import type { UserInteraction, SessionEvent } from '../../../static/iron-remote-desktop';
|
||||
import { userInteractionService } from '../../services/session.service';
|
||||
import type { UserInteraction } from '../../../static/iron-remote-desktop';
|
||||
import { Backend } from '../../../static/iron-remote-desktop-rdp';
|
||||
import { preConnectionBlob, displayControl, kdcProxyUrl } from '../../../static/iron-remote-desktop-rdp';
|
||||
|
||||
|
|
@ -9,20 +9,6 @@
|
|||
let cursorOverrideActive = false;
|
||||
let showUtilityBar = false;
|
||||
|
||||
userInteractionService.subscribe((userInteraction) => {
|
||||
if (userInteraction != null) {
|
||||
const callback = (event: SessionEvent) => {
|
||||
if (event.type === 0) {
|
||||
userInteraction.setVisibility(true);
|
||||
} else if (event.type === 1) {
|
||||
setCurrentSessionActive(false);
|
||||
}
|
||||
};
|
||||
|
||||
userInteraction.onSessionEvent(callback);
|
||||
}
|
||||
});
|
||||
|
||||
userInteractionService.subscribe((uis) => {
|
||||
if (uis != null) {
|
||||
userInteraction = uis;
|
||||
|
|
|
|||
|
|
@ -1,6 +1,6 @@
|
|||
<script lang="ts">
|
||||
import { onMount } from 'svelte';
|
||||
import { setCurrentSessionActive, userInteractionService } from '../../services/session.service';
|
||||
import { userInteractionService } from '../../services/session.service';
|
||||
import { showLogin } from '$lib/login/login-store';
|
||||
import type { UserInteraction } from '../../../static/iron-remote-desktop';
|
||||
import { Backend } from '../../../static/iron-remote-desktop-rdp';
|
||||
|
|
@ -12,14 +12,6 @@
|
|||
userInteractionService.subscribe((uis) => {
|
||||
if (uis != null) {
|
||||
uiService = uis;
|
||||
uiService.onSessionEvent((event) => {
|
||||
if (event.type === 0) {
|
||||
uiService.setVisibility(true);
|
||||
} else if (event.type === 1) {
|
||||
setCurrentSessionActive(false);
|
||||
showLogin.set(true);
|
||||
}
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue