mirror of
https://github.com/GNS3/gns3-web-ui.git
synced 2025-04-16 14:38:52 +00:00
Web console in new tab
This commit is contained in:
parent
2d8c5966cd
commit
48dbd7f6aa
@ -63,6 +63,7 @@ import { DirectLinkComponent } from './components/direct-link/direct-link.compon
|
||||
import { SystemStatusComponent } from './components/system-status/system-status.component';
|
||||
import { ServerResolve } from './resolvers/server-resolve';
|
||||
import { ProjectMapGuard } from './guards/project-map-guard';
|
||||
import { WebConsoleFullWindowComponent } from './components/web-console-full-window/web-console-full-window.component';
|
||||
|
||||
const routes: Routes = [
|
||||
{
|
||||
@ -154,6 +155,11 @@ const routes: Routes = [
|
||||
component: ProjectMapComponent,
|
||||
canActivate: [ProjectMapGuard]
|
||||
},
|
||||
{
|
||||
path: 'server/:server_id/project/:project_id/nodes/:node_id',
|
||||
component: WebConsoleFullWindowComponent,
|
||||
canActivate: [ProjectMapGuard]
|
||||
},
|
||||
{
|
||||
path: '**',
|
||||
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 { ConsoleWrapperComponent } from './components/project-map/console-wrapper/console-wrapper.component';
|
||||
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) {
|
||||
Raven.config('https://b2b1cfd9b043491eb6b566fd8acee358@sentry.io/842726', {
|
||||
@ -456,7 +458,9 @@ if (environment.production) {
|
||||
OpenFileExplorerActionComponent,
|
||||
HttpConsoleActionComponent,
|
||||
WebConsoleComponent,
|
||||
ConsoleWrapperComponent
|
||||
ConsoleWrapperComponent,
|
||||
HttpConsoleNewTabActionComponent,
|
||||
WebConsoleFullWindowComponent
|
||||
],
|
||||
imports: [
|
||||
BrowserModule,
|
||||
|
@ -2,6 +2,8 @@ import { Component, OnInit, OnDestroy, ComponentFactoryResolver, ViewContainerRe
|
||||
import { timer, Observable, Subscription } from 'rxjs';
|
||||
import { ThemeService } from '../../services/theme.service';
|
||||
import { AdbutlerComponent } from '../adbutler/adbutler.component';
|
||||
import { Router } from '@angular/router';
|
||||
import { Location } from '@angular/common';
|
||||
|
||||
@Component({
|
||||
selector: 'app-notification-box',
|
||||
@ -31,11 +33,12 @@ export class NotificationBoxComponent implements OnInit, OnDestroy {
|
||||
constructor(
|
||||
private themeService: ThemeService,
|
||||
private componentFactoryResolver: ComponentFactoryResolver,
|
||||
private viewContainerRef: ViewContainerRef
|
||||
){}
|
||||
private viewContainerRef: ViewContainerRef,
|
||||
private location: Location
|
||||
) {}
|
||||
|
||||
ngOnInit() {
|
||||
this.startTimer();
|
||||
if (!this.location.path().includes('nodes')) this.startTimer();
|
||||
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`,
|
||||
width: `${event.rectangle.width}px`
|
||||
};
|
||||
|
||||
this.consoleService.resizeTerminal();
|
||||
}
|
||||
|
||||
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"
|
||||
[nodes]="nodes"
|
||||
></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
|
||||
*ngIf="!projectService.isReadOnly(project) && nodes.length && isElectronApp"
|
||||
[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 {
|
||||
public nodeConsoleTrigger = new EventEmitter<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() {}
|
||||
|
||||
@ -18,7 +24,20 @@ export class NodeConsoleService {
|
||||
this.closeNodeConsoleTrigger.next(node);
|
||||
}
|
||||
|
||||
resizeTerminal() {
|
||||
this.consoleResized.next(true);
|
||||
resizeTerminal(event: ConsoleResizedEvent) {
|
||||
this.consoleResized.next(event);
|
||||
}
|
||||
|
||||
getLineWidth() {
|
||||
return this.defaultConsoleWidth / this.defaultNumberOfColumns;
|
||||
}
|
||||
|
||||
getLineHeight() {
|
||||
return this.defaultConsoleHeight / this.defaultNumberOfRows;
|
||||
}
|
||||
}
|
||||
|
||||
export interface ConsoleResizedEvent {
|
||||
numberOfColumns: number,
|
||||
numberOfRows: number
|
||||
}
|
||||
|
Loading…
x
Reference in New Issue
Block a user