mirror of
https://github.com/GNS3/gns3-web-ui.git
synced 2025-06-18 23:08:14 +00:00
Web console in new tab
This commit is contained in:
@ -63,6 +63,7 @@ import { DirectLinkComponent } from './components/direct-link/direct-link.compon
|
|||||||
import { SystemStatusComponent } from './components/system-status/system-status.component';
|
import { SystemStatusComponent } from './components/system-status/system-status.component';
|
||||||
import { ServerResolve } from './resolvers/server-resolve';
|
import { ServerResolve } from './resolvers/server-resolve';
|
||||||
import { ProjectMapGuard } from './guards/project-map-guard';
|
import { ProjectMapGuard } from './guards/project-map-guard';
|
||||||
|
import { WebConsoleFullWindowComponent } from './components/web-console-full-window/web-console-full-window.component';
|
||||||
|
|
||||||
const routes: Routes = [
|
const routes: Routes = [
|
||||||
{
|
{
|
||||||
@ -154,6 +155,11 @@ const routes: Routes = [
|
|||||||
component: ProjectMapComponent,
|
component: ProjectMapComponent,
|
||||||
canActivate: [ProjectMapGuard]
|
canActivate: [ProjectMapGuard]
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
path: 'server/:server_id/project/:project_id/nodes/:node_id',
|
||||||
|
component: WebConsoleFullWindowComponent,
|
||||||
|
canActivate: [ProjectMapGuard]
|
||||||
|
},
|
||||||
{
|
{
|
||||||
path: '**',
|
path: '**',
|
||||||
component: PageNotFoundComponent
|
component: PageNotFoundComponent
|
||||||
|
@ -272,6 +272,8 @@ import { HttpConsoleActionComponent } from './components/project-map/context-men
|
|||||||
import { WebConsoleComponent } from './components/project-map/web-console/web-console.component';
|
import { WebConsoleComponent } from './components/project-map/web-console/web-console.component';
|
||||||
import { ConsoleWrapperComponent } from './components/project-map/console-wrapper/console-wrapper.component';
|
import { ConsoleWrapperComponent } from './components/project-map/console-wrapper/console-wrapper.component';
|
||||||
import { NodeConsoleService } from './services/nodeConsole.service';
|
import { NodeConsoleService } from './services/nodeConsole.service';
|
||||||
|
import { HttpConsoleNewTabActionComponent } from './components/project-map/context-menu/actions/http-console-new-tab/http-console-new-tab-action.component';
|
||||||
|
import { WebConsoleFullWindowComponent } from './components/web-console-full-window/web-console-full-window.component';
|
||||||
|
|
||||||
if (environment.production) {
|
if (environment.production) {
|
||||||
Raven.config('https://b2b1cfd9b043491eb6b566fd8acee358@sentry.io/842726', {
|
Raven.config('https://b2b1cfd9b043491eb6b566fd8acee358@sentry.io/842726', {
|
||||||
@ -456,7 +458,9 @@ if (environment.production) {
|
|||||||
OpenFileExplorerActionComponent,
|
OpenFileExplorerActionComponent,
|
||||||
HttpConsoleActionComponent,
|
HttpConsoleActionComponent,
|
||||||
WebConsoleComponent,
|
WebConsoleComponent,
|
||||||
ConsoleWrapperComponent
|
ConsoleWrapperComponent,
|
||||||
|
HttpConsoleNewTabActionComponent,
|
||||||
|
WebConsoleFullWindowComponent
|
||||||
],
|
],
|
||||||
imports: [
|
imports: [
|
||||||
BrowserModule,
|
BrowserModule,
|
||||||
|
@ -2,6 +2,8 @@ import { Component, OnInit, OnDestroy, ComponentFactoryResolver, ViewContainerRe
|
|||||||
import { timer, Observable, Subscription } from 'rxjs';
|
import { timer, Observable, Subscription } from 'rxjs';
|
||||||
import { ThemeService } from '../../services/theme.service';
|
import { ThemeService } from '../../services/theme.service';
|
||||||
import { AdbutlerComponent } from '../adbutler/adbutler.component';
|
import { AdbutlerComponent } from '../adbutler/adbutler.component';
|
||||||
|
import { Router } from '@angular/router';
|
||||||
|
import { Location } from '@angular/common';
|
||||||
|
|
||||||
@Component({
|
@Component({
|
||||||
selector: 'app-notification-box',
|
selector: 'app-notification-box',
|
||||||
@ -31,11 +33,12 @@ export class NotificationBoxComponent implements OnInit, OnDestroy {
|
|||||||
constructor(
|
constructor(
|
||||||
private themeService: ThemeService,
|
private themeService: ThemeService,
|
||||||
private componentFactoryResolver: ComponentFactoryResolver,
|
private componentFactoryResolver: ComponentFactoryResolver,
|
||||||
private viewContainerRef: ViewContainerRef
|
private viewContainerRef: ViewContainerRef,
|
||||||
){}
|
private location: Location
|
||||||
|
) {}
|
||||||
|
|
||||||
ngOnInit() {
|
ngOnInit() {
|
||||||
this.startTimer();
|
if (!this.location.path().includes('nodes')) this.startTimer();
|
||||||
this.themeService.getActualTheme() === 'light' ? this.isLightThemeEnabled = true : this.isLightThemeEnabled = false;
|
this.themeService.getActualTheme() === 'light' ? this.isLightThemeEnabled = true : this.isLightThemeEnabled = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -119,8 +119,6 @@ export class ConsoleWrapperComponent implements OnInit {
|
|||||||
height: `${event.rectangle.height - 60}px`,
|
height: `${event.rectangle.height - 60}px`,
|
||||||
width: `${event.rectangle.width}px`
|
width: `${event.rectangle.width}px`
|
||||||
};
|
};
|
||||||
|
|
||||||
this.consoleService.resizeTerminal();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
close() {
|
close() {
|
||||||
|
@ -0,0 +1,4 @@
|
|||||||
|
<button mat-menu-item (click)="openConsole()">
|
||||||
|
<mat-icon>http</mat-icon>
|
||||||
|
<span>Web console in new tab</span>
|
||||||
|
</button>
|
@ -0,0 +1,32 @@
|
|||||||
|
import { Component, OnInit, Input } from '@angular/core';
|
||||||
|
import { Node } from '../../../../../cartography/models/node';
|
||||||
|
import { Server } from '../../../../../models/server';
|
||||||
|
import { ToasterService } from '../../../../../services/toaster.service';
|
||||||
|
import { Router } from '@angular/router';
|
||||||
|
|
||||||
|
|
||||||
|
@Component({
|
||||||
|
selector: 'app-http-console-new-tab-action',
|
||||||
|
templateUrl: './http-console-new-tab-action.component.html'
|
||||||
|
})
|
||||||
|
export class HttpConsoleNewTabActionComponent implements OnInit {
|
||||||
|
@Input() server: Server;
|
||||||
|
@Input() nodes: Node[];
|
||||||
|
|
||||||
|
constructor(
|
||||||
|
private toasterService: ToasterService,
|
||||||
|
private router: Router
|
||||||
|
) { }
|
||||||
|
|
||||||
|
ngOnInit() {}
|
||||||
|
|
||||||
|
openConsole() {
|
||||||
|
this.nodes.forEach(n => {
|
||||||
|
if (n.status === 'started') {
|
||||||
|
window.open(`${this.router.url}/nodes/${n.node_id}`);
|
||||||
|
} else {
|
||||||
|
this.toasterService.error('To open console please start the node');
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
@ -20,6 +20,11 @@
|
|||||||
[server]="server"
|
[server]="server"
|
||||||
[nodes]="nodes"
|
[nodes]="nodes"
|
||||||
></app-http-console-action>
|
></app-http-console-action>
|
||||||
|
<app-http-console-new-tab-action
|
||||||
|
*ngIf="!projectService.isReadOnly(project) && nodes.length"
|
||||||
|
[server]="server"
|
||||||
|
[nodes]="nodes"
|
||||||
|
></app-http-console-new-tab-action>
|
||||||
<app-console-device-action
|
<app-console-device-action
|
||||||
*ngIf="!projectService.isReadOnly(project) && nodes.length && isElectronApp"
|
*ngIf="!projectService.isReadOnly(project) && nodes.length && isElectronApp"
|
||||||
[server]="server"
|
[server]="server"
|
||||||
|
@ -0,0 +1 @@
|
|||||||
|
<div style="width: 100%;height: 100%;" #terminal id="terminal"></div>
|
@ -0,0 +1,73 @@
|
|||||||
|
import { Component, OnInit, Input, AfterViewInit, ViewEncapsulation, ViewChild, ElementRef } from '@angular/core';
|
||||||
|
import { Project } from '../../models/project';
|
||||||
|
import { Server } from '../../models/server';
|
||||||
|
import { Terminal } from 'xterm';
|
||||||
|
import { AttachAddon } from 'xterm-addon-attach';
|
||||||
|
import { Node } from '../../cartography/models/node';
|
||||||
|
import { FitAddon } from 'xterm-addon-fit';
|
||||||
|
import { NodeConsoleService } from '../../services/nodeConsole.service';
|
||||||
|
import { ActivatedRoute } from '@angular/router';
|
||||||
|
import { ServerService } from '../../services/server.service';
|
||||||
|
|
||||||
|
|
||||||
|
@Component({
|
||||||
|
encapsulation: ViewEncapsulation.None,
|
||||||
|
selector: 'app-web-console-full-window',
|
||||||
|
templateUrl: './web-console-full-window.component.html',
|
||||||
|
styleUrls: ['../../../../../node_modules/xterm/css/xterm.css']
|
||||||
|
})
|
||||||
|
export class WebConsoleFullWindowComponent implements OnInit, AfterViewInit {
|
||||||
|
private serverId: string;
|
||||||
|
private projectId: string;
|
||||||
|
private nodeId: string;
|
||||||
|
|
||||||
|
public term: Terminal = new Terminal();
|
||||||
|
public fitAddon: FitAddon = new FitAddon();
|
||||||
|
|
||||||
|
@ViewChild('terminal', {static: false}) terminal: ElementRef;
|
||||||
|
|
||||||
|
constructor(
|
||||||
|
private consoleService: NodeConsoleService,
|
||||||
|
private serverService: ServerService,
|
||||||
|
private route: ActivatedRoute
|
||||||
|
) {}
|
||||||
|
|
||||||
|
ngOnInit() {
|
||||||
|
this.serverId = this.route.snapshot.paramMap.get("server_id");
|
||||||
|
this.projectId = this.route.snapshot.paramMap.get("project_id");
|
||||||
|
this.nodeId = this.route.snapshot.paramMap.get("node_id");
|
||||||
|
|
||||||
|
this.consoleService.consoleResized.subscribe(ev => {
|
||||||
|
this.fitAddon.fit();
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
async ngAfterViewInit() {
|
||||||
|
this.term.open(this.terminal.nativeElement);
|
||||||
|
const socket = new WebSocket(await this.getUrl());
|
||||||
|
|
||||||
|
socket.onerror = ((event) => {
|
||||||
|
this.term.write("Connection lost" + "\r\n");
|
||||||
|
});
|
||||||
|
socket.onclose = ((event) => {
|
||||||
|
this.term.write("Connection closed" + "\r\n");
|
||||||
|
});
|
||||||
|
|
||||||
|
const attachAddon = new AttachAddon(socket);
|
||||||
|
this.term.loadAddon(attachAddon);
|
||||||
|
this.term.setOption('cursorBlink', true);
|
||||||
|
this.term.loadAddon(this.fitAddon);
|
||||||
|
this.fitAddon.activate(this.term);
|
||||||
|
this.fitAddon.fit();
|
||||||
|
this.term.focus();
|
||||||
|
|
||||||
|
let numberOfColumns = Math.round(window.innerWidth / this.consoleService.getLineWidth());
|
||||||
|
let numberOfRows = Math.round(window.innerHeight / this.consoleService.getLineHeight());
|
||||||
|
this.term.resize(numberOfColumns, numberOfRows);
|
||||||
|
}
|
||||||
|
|
||||||
|
async getUrl() {
|
||||||
|
let server: Server = await this.serverService.get(+this.serverId);
|
||||||
|
return `ws://${server.host}:${server.port}/v2/projects/${this.projectId}/nodes/${this.nodeId}/console/ws`
|
||||||
|
}
|
||||||
|
}
|
@ -6,7 +6,13 @@ import { Subject } from 'rxjs';
|
|||||||
export class NodeConsoleService {
|
export class NodeConsoleService {
|
||||||
public nodeConsoleTrigger = new EventEmitter<Node>();
|
public nodeConsoleTrigger = new EventEmitter<Node>();
|
||||||
public closeNodeConsoleTrigger = new Subject<Node>();
|
public closeNodeConsoleTrigger = new Subject<Node>();
|
||||||
public consoleResized = new Subject<boolean>();
|
public consoleResized = new Subject<ConsoleResizedEvent>();
|
||||||
|
|
||||||
|
public readonly defaultConsoleWidth = 720;
|
||||||
|
public readonly defaultConsoleHeight = 408;
|
||||||
|
|
||||||
|
public readonly defaultNumberOfColumns = 80;
|
||||||
|
public readonly defaultNumberOfRows = 24;
|
||||||
|
|
||||||
constructor() {}
|
constructor() {}
|
||||||
|
|
||||||
@ -18,7 +24,20 @@ export class NodeConsoleService {
|
|||||||
this.closeNodeConsoleTrigger.next(node);
|
this.closeNodeConsoleTrigger.next(node);
|
||||||
}
|
}
|
||||||
|
|
||||||
resizeTerminal() {
|
resizeTerminal(event: ConsoleResizedEvent) {
|
||||||
this.consoleResized.next(true);
|
this.consoleResized.next(event);
|
||||||
|
}
|
||||||
|
|
||||||
|
getLineWidth() {
|
||||||
|
return this.defaultConsoleWidth / this.defaultNumberOfColumns;
|
||||||
|
}
|
||||||
|
|
||||||
|
getLineHeight() {
|
||||||
|
return this.defaultConsoleHeight / this.defaultNumberOfRows;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export interface ConsoleResizedEvent {
|
||||||
|
numberOfColumns: number,
|
||||||
|
numberOfRows: number
|
||||||
|
}
|
||||||
|
Reference in New Issue
Block a user