Web console in new tab

This commit is contained in:
piotrpekala7 2020-04-01 00:07:19 +02:00
parent 2d8c5966cd
commit 48dbd7f6aa
11 changed files with 154 additions and 9 deletions

View File

@ -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

View File

@ -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,

View File

@ -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;
}

View File

@ -119,8 +119,6 @@ export class ConsoleWrapperComponent implements OnInit {
height: `${event.rectangle.height - 60}px`,
width: `${event.rectangle.width}px`
};
this.consoleService.resizeTerminal();
}
close() {

View File

@ -0,0 +1,4 @@
<button mat-menu-item (click)="openConsole()">
<mat-icon>http</mat-icon>
<span>Web console in new tab</span>
</button>

View File

@ -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');
}
});
}
}

View File

@ -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"

View File

@ -0,0 +1 @@
<div style="width: 100%;height: 100%;" #terminal id="terminal"></div>

View File

@ -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`
}
}

View File

@ -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
}