diff --git a/.angular-cli.json b/.angular-cli.json
index 681e742ba6..b9a440bdba 100644
--- a/.angular-cli.json
+++ b/.angular-cli.json
@@ -18,7 +18,8 @@
"testTsconfig": "tsconfig.spec.json",
"prefix": "app",
"styles": [
- "styles.scss"
+ "styles.scss",
+ "../node_modules/xterm/dist/xterm.css"
],
"scripts": [],
"environmentSource": "environments/environment.ts",
diff --git a/package.json b/package.json
index 017fa7b0f0..4af4dc1b97 100644
--- a/package.json
+++ b/package.json
@@ -45,6 +45,7 @@
"rxjs": "^5.5.5",
"rxjs-spy": "^6.1.0",
"rxjs-websockets": "^4.0.0",
+ "xterm": "^2.9.1",
"zone.js": "^0.8.14"
},
"devDependencies": {
@@ -55,6 +56,7 @@
"@types/jasminewd2": "~2.0.2",
"@types/karma": "^1.7.1",
"@types/node": "~6.0.60",
+ "@types/xterm": "^2.0.3",
"codecov": "^3.0.0",
"codelyzer": "~3.1.1",
"jasmine-core": "~2.6.2",
diff --git a/proxy.conf.template.js b/proxy.conf.template.js
index 6a7e588438..6f09a5b6a1 100644
--- a/proxy.conf.template.js
+++ b/proxy.conf.template.js
@@ -5,6 +5,7 @@ const PROXY_CONFIG = {
"protocol": "https:",
"port": 443
},
+ "ws": true,
"secure": false,
"changeOrigin": true,
"ws": true
diff --git a/src/app/features/applications/application/application-tabs-base/application-tabs-base.component.ts b/src/app/features/applications/application/application-tabs-base/application-tabs-base.component.ts
index 90f6e14d2d..6c9e9d5e38 100644
--- a/src/app/features/applications/application/application-tabs-base/application-tabs-base.component.ts
+++ b/src/app/features/applications/application/application-tabs-base/application-tabs-base.component.ts
@@ -56,7 +56,6 @@ export class ApplicationTabsBaseComponent implements OnInit, OnDestroy {
{ link: 'services', label: 'Services' },
{ link: 'variables', label: 'Variables' },
{ link: 'events', label: 'Events' },
- { link: 'ssh', label: 'SSH' }
];
autoRefreshString = 'auto-refresh';
diff --git a/src/app/features/applications/application/application-tabs-base/tabs/ssh-tab/ssh-tab.component.html b/src/app/features/applications/application/application-tabs-base/tabs/ssh-tab/ssh-tab.component.html
deleted file mode 100644
index d16c382061..0000000000
--- a/src/app/features/applications/application/application-tabs-base/tabs/ssh-tab/ssh-tab.component.html
+++ /dev/null
@@ -1,3 +0,0 @@
-
- ssh-tab works!
-
diff --git a/src/app/features/applications/application/application-tabs-base/tabs/ssh-tab/ssh-tab.component.spec.ts b/src/app/features/applications/application/application-tabs-base/tabs/ssh-tab/ssh-tab.component.spec.ts
deleted file mode 100644
index d5bd72deff..0000000000
--- a/src/app/features/applications/application/application-tabs-base/tabs/ssh-tab/ssh-tab.component.spec.ts
+++ /dev/null
@@ -1,25 +0,0 @@
-import { async, ComponentFixture, TestBed } from '@angular/core/testing';
-
-import { SshTabComponent } from './ssh-tab.component';
-
-describe('SshTabComponent', () => {
- let component: SshTabComponent;
- let fixture: ComponentFixture;
-
- beforeEach(async(() => {
- TestBed.configureTestingModule({
- declarations: [ SshTabComponent ]
- })
- .compileComponents();
- }));
-
- beforeEach(() => {
- fixture = TestBed.createComponent(SshTabComponent);
- component = fixture.componentInstance;
- fixture.detectChanges();
- });
-
- it('should be created', () => {
- expect(component).toBeTruthy();
- });
-});
diff --git a/src/app/features/applications/application/application-tabs-base/tabs/ssh-tab/ssh-tab.component.ts b/src/app/features/applications/application/application-tabs-base/tabs/ssh-tab/ssh-tab.component.ts
deleted file mode 100644
index a52479bd14..0000000000
--- a/src/app/features/applications/application/application-tabs-base/tabs/ssh-tab/ssh-tab.component.ts
+++ /dev/null
@@ -1,15 +0,0 @@
-import { Component, OnInit } from '@angular/core';
-
-@Component({
- selector: 'app-ssh-tab',
- templateUrl: './ssh-tab.component.html',
- styleUrls: ['./ssh-tab.component.scss']
-})
-export class SshTabComponent implements OnInit {
-
- constructor() { }
-
- ngOnInit() {
- }
-
-}
diff --git a/src/app/features/applications/applications.module.ts b/src/app/features/applications/applications.module.ts
index addd89c3df..a2517b4563 100644
--- a/src/app/features/applications/applications.module.ts
+++ b/src/app/features/applications/applications.module.ts
@@ -7,7 +7,6 @@ import { ApplicationBaseComponent } from './application/application-base.compone
import { EventsTabComponent } from './application/application-tabs-base/tabs/events-tab/events-tab.component';
import { LogStreamTabComponent } from './application/application-tabs-base/tabs/log-stream-tab/log-stream-tab.component';
import { ServicesTabComponent } from './application/application-tabs-base/tabs/services-tab/services-tab.component';
-import { SshTabComponent } from './application/application-tabs-base/tabs/ssh-tab/ssh-tab.component';
import { ApplicationEnvVarsService } from './application/application-tabs-base/tabs/build-tab/application-env-vars.service';
import { BuildTabComponent } from './application/application-tabs-base/tabs/build-tab/build-tab.component';
import { ViewBuildpackComponent } from './application/application-tabs-base/tabs/build-tab/view-buildpack/view-buildpack.component';
@@ -16,6 +15,7 @@ import { ApplicationsRoutingModule } from './applications.routing';
import { ApplicationTabsBaseComponent } from './application/application-tabs-base/application-tabs-base.component';
import { DatePipe } from '@angular/common';
import { EditApplicationComponent } from './edit-application/edit-application.component';
+import { SshApplicationComponent } from './ssh-application/ssh-application.component';
import { InstancesTabComponent } from './application/application-tabs-base/tabs/instances-tab/instances-tab.component';
import { ApplicationMonitorService } from './application-monitor.service';
import { RoutesComponent } from './routes/routes.component';
@@ -33,15 +33,15 @@ import { AddRoutesComponent } from './routes/add-routes/add-routes.component';
EventsTabComponent,
LogStreamTabComponent,
ServicesTabComponent,
- SshTabComponent,
BuildTabComponent,
VariablesTabComponent,
ViewBuildpackComponent,
ApplicationTabsBaseComponent,
+ SshApplicationComponent,
RoutesComponent,
EditApplicationComponent,
InstancesTabComponent,
- AddRoutesComponent
+ AddRoutesComponent,
],
providers: [
ApplicationService,
diff --git a/src/app/features/applications/applications.routing.ts b/src/app/features/applications/applications.routing.ts
index 62b7871383..af708cda75 100644
--- a/src/app/features/applications/applications.routing.ts
+++ b/src/app/features/applications/applications.routing.ts
@@ -8,17 +8,17 @@ import { ApplicationBaseComponent } from './application/application-base.compone
import { EventsTabComponent } from './application/application-tabs-base/tabs/events-tab/events-tab.component';
import { LogStreamTabComponent } from './application/application-tabs-base/tabs/log-stream-tab/log-stream-tab.component';
import { ServicesTabComponent } from './application/application-tabs-base/tabs/services-tab/services-tab.component';
-import { SshTabComponent } from './application/application-tabs-base/tabs/ssh-tab/ssh-tab.component';
import { BuildTabComponent } from './application/application-tabs-base/tabs/build-tab/build-tab.component';
import { VariablesTabComponent } from './application/application-tabs-base/tabs/variables-tab/variables-tab.component';
import { CreateApplicationComponent } from './create-application/create-application.component';
import { CreateApplicationModule } from './create-application/create-application.module';
+import { SshApplicationComponent } from './ssh-application/ssh-application.component';
import { EditApplicationComponent } from './edit-application/edit-application.component';
import { ApplicationTabsBaseComponent } from './application/application-tabs-base/application-tabs-base.component';
const appplicationsRoutes: Routes = [
{
path: 'new',
- component: CreateApplicationComponent
+ component: CreateApplicationComponent,
},
{
path: '',
@@ -34,7 +34,11 @@ const appplicationsRoutes: Routes = [
children: [
{
path: 'edit',
- component: EditApplicationComponent
+ component: EditApplicationComponent,
+ },
+ {
+ path: 'ssh/:index',
+ component: SshApplicationComponent,
},
{
path: '',
@@ -50,7 +54,6 @@ const appplicationsRoutes: Routes = [
{ path: 'services', component: ServicesTabComponent },
{ path: 'variables', component: VariablesTabComponent },
{ path: 'events', component: EventsTabComponent },
- { path: 'ssh', component: SshTabComponent }
]
},
{
diff --git a/src/app/features/applications/ssh-application/ssh-application.component.html b/src/app/features/applications/ssh-application/ssh-application.component.html
new file mode 100644
index 0000000000..6e500a5970
--- /dev/null
+++ b/src/app/features/applications/ssh-application/ssh-application.component.html
@@ -0,0 +1,18 @@
+
+ Application SSH ({{ (applicationService.app$ | async)?.entity?.entity.name }} #{{ instanceId }})
+
+
+
+
diff --git a/src/app/features/applications/application/application-tabs-base/tabs/ssh-tab/ssh-tab.component.scss b/src/app/features/applications/ssh-application/ssh-application.component.scss
similarity index 100%
rename from src/app/features/applications/application/application-tabs-base/tabs/ssh-tab/ssh-tab.component.scss
rename to src/app/features/applications/ssh-application/ssh-application.component.scss
diff --git a/src/app/features/applications/ssh-application/ssh-application.component.spec.ts b/src/app/features/applications/ssh-application/ssh-application.component.spec.ts
new file mode 100644
index 0000000000..ca626a047e
--- /dev/null
+++ b/src/app/features/applications/ssh-application/ssh-application.component.spec.ts
@@ -0,0 +1,40 @@
+import { async, ComponentFixture, TestBed } from '@angular/core/testing';
+
+import { SshApplicationComponent } from './ssh-application.component';
+import { SharedModule } from '../../../shared/shared.module';
+import { CoreModule } from '../../../core/core.module';
+import { RouterTestingModule } from '@angular/router/testing';
+import { createBasicStoreModule } from '../../../test-framework/store-test-helper';
+import { ApplicationService } from '../application.service';
+import { ApplicationServiceMock } from '../../../test-framework/application-service-helper';
+
+describe('SshApplicationComponent', () => {
+ let component: SshApplicationComponent;
+ let fixture: ComponentFixture;
+
+ beforeEach(async(() => {
+ TestBed.configureTestingModule({
+ declarations: [ SshApplicationComponent ],
+ imports: [
+ CoreModule,
+ SharedModule,
+ RouterTestingModule,
+ createBasicStoreModule()
+ ],
+ providers: [
+ { provide: ApplicationService, useClass: ApplicationServiceMock },
+ ]
+ })
+ .compileComponents();
+ }));
+
+ beforeEach(() => {
+ fixture = TestBed.createComponent(SshApplicationComponent);
+ component = fixture.componentInstance;
+ fixture.detectChanges();
+ });
+
+ it('should create', () => {
+ expect(component).toBeTruthy();
+ });
+});
diff --git a/src/app/features/applications/ssh-application/ssh-application.component.ts b/src/app/features/applications/ssh-application/ssh-application.component.ts
new file mode 100644
index 0000000000..d2e2e372e7
--- /dev/null
+++ b/src/app/features/applications/ssh-application/ssh-application.component.ts
@@ -0,0 +1,87 @@
+import { Component, OnInit, ViewChild } from '@angular/core';
+import { ActivatedRoute, Router } from '@angular/router';
+
+import { Observable } from 'rxjs/Observable';
+import { Store } from '@ngrx/store';
+import { Subscription, Subject } from 'rxjs/Rx';
+import websocketConnect from 'rxjs-websockets';
+
+import { ApplicationService } from '../application.service';
+import { QueueingSubject } from 'queueing-subject';
+import { LoggerService } from '../../../core/logger.service';
+import { SshViewerComponent } from '../../../shared/components/ssh-viewer/ssh-viewer.component';
+import { ShowSnackBar } from '../../../store/actions/snackBar.actions';
+import { AppState } from '../../../store/app-state';
+import { EntityService } from '../../../core/entity-service';
+import { GetApplication, ApplicationSchema } from '../../../store/actions/application.actions';
+
+@Component({
+ selector: 'app-ssh-application',
+ templateUrl: './ssh-application.component.html',
+ styleUrls: ['./ssh-application.component.scss'],
+})
+export class SshApplicationComponent implements OnInit {
+
+ public messages: Observable;
+
+ public connectionStatus: Observable;
+
+ public sshInput: QueueingSubject;
+
+ public errorMessage: string;
+
+ public sshRoute: string;
+
+ public connected: boolean;
+
+ public appInstanceLink: string;
+
+ private connection: Subscription;
+
+ public instanceId: string;
+
+ @ViewChild('sshViewer') sshViewer: SshViewerComponent;
+
+ constructor(
+ private activatedRoute: ActivatedRoute,
+ private store: Store,
+ private applicationService: ApplicationService,
+ ) { }
+
+ ngOnInit() {
+
+ const { cfGuid, appGuid } = this.applicationService;
+ const routeParams = this.activatedRoute.snapshot.params;
+ this.instanceId = routeParams.index;
+
+ this.appInstanceLink = (
+ `/applications/${cfGuid}/${appGuid}/instances`
+ );
+
+ if (!cfGuid || !appGuid || !this.instanceId) {
+ this.messages = Observable.never();
+ this.connectionStatus = Observable.never();
+ } else {
+ const host = window.location.host;
+ const protocol = window.location.protocol === 'https:' ? 'wss' : 'ws';
+ const streamUrl = (
+ `${protocol}://${host}/pp/v1/${cfGuid}/apps/${appGuid}/ssh/${this.instanceId}`
+ );
+ this.sshInput = new QueueingSubject();
+ const connection = websocketConnect(
+ streamUrl,
+ this.sshInput
+ );
+
+ this.messages = connection.messages
+ .catch(e => {
+ if (e.type === 'error') {
+ this.errorMessage = 'Error connecting to web socket';
+ }
+ return [];
+ });
+
+ this.connectionStatus = connection.connectionStatus;
+ }
+ }
+}
diff --git a/src/app/shared/components/ssh-viewer/ssh-viewer.component.html b/src/app/shared/components/ssh-viewer/ssh-viewer.component.html
new file mode 100644
index 0000000000..3d71c6e1a8
--- /dev/null
+++ b/src/app/shared/components/ssh-viewer/ssh-viewer.component.html
@@ -0,0 +1,15 @@
+
+
+
+ Error occurred establishing SSH connection
+
+
+ Disconnected
+
+
+
+
+
+
\ No newline at end of file
diff --git a/src/app/shared/components/ssh-viewer/ssh-viewer.component.scss b/src/app/shared/components/ssh-viewer/ssh-viewer.component.scss
new file mode 100644
index 0000000000..000e0340e2
--- /dev/null
+++ b/src/app/shared/components/ssh-viewer/ssh-viewer.component.scss
@@ -0,0 +1,46 @@
+.ssh-viewer {
+ display: flex;
+ flex-direction: column;
+ height: 100%;
+ position: relative;
+
+ .ssh-terminal {
+ display: flex;
+ flex: 1;
+ flex-direction: column;
+ opacity: 1;
+ transition: opacity .5s ease-in-out;
+
+ &.ssh-terminal-disconnected {
+ opacity: .4;
+ }
+
+ > div {
+ display: flex;
+ flex: 1;
+ flex-direction: column;
+ }
+ }
+
+ &__overlay {
+ align-items: flex-end;
+ display: flex;
+ height: 100%;
+ justify-content: center;
+ position: absolute;
+ width: 100%;
+ z-index: 1;
+ }
+
+ &__error {
+ padding: 8px;
+ text-align: center;
+ width: 100%;
+ }
+
+ &__disconnected {
+ padding: 8px;
+ text-align: center;
+ width: 100%;
+ }
+}
diff --git a/src/app/shared/components/ssh-viewer/ssh-viewer.component.spec.ts b/src/app/shared/components/ssh-viewer/ssh-viewer.component.spec.ts
new file mode 100644
index 0000000000..ca7099634c
--- /dev/null
+++ b/src/app/shared/components/ssh-viewer/ssh-viewer.component.spec.ts
@@ -0,0 +1,29 @@
+import { async, ComponentFixture, TestBed } from '@angular/core/testing';
+
+import { SshViewerComponent } from './ssh-viewer.component';
+import { CoreModule } from '../../../core/core.module';
+
+describe('SshViewerComponent', () => {
+ let component: SshViewerComponent;
+ let fixture: ComponentFixture;
+
+ beforeEach(async(() => {
+ TestBed.configureTestingModule({
+ declarations: [ SshViewerComponent ],
+ imports: [
+ CoreModule,
+ ]
+ })
+ .compileComponents();
+ }));
+
+ beforeEach(() => {
+ fixture = TestBed.createComponent(SshViewerComponent);
+ component = fixture.componentInstance;
+ fixture.detectChanges();
+ });
+
+ it('should create', () => {
+ expect(component).toBeTruthy();
+ });
+});
diff --git a/src/app/shared/components/ssh-viewer/ssh-viewer.component.theme.scss b/src/app/shared/components/ssh-viewer/ssh-viewer.component.theme.scss
new file mode 100644
index 0000000000..9e352fb41c
--- /dev/null
+++ b/src/app/shared/components/ssh-viewer/ssh-viewer.component.theme.scss
@@ -0,0 +1,27 @@
+@mixin app-ssh-viewer-theme($theme, $app-theme) {
+ $primary: map-get($theme, primary);
+ $foreground-colors: map-get($theme, foreground);
+ $status-colors: map-get($app-theme, status);
+ $ssh-error: map-get($status-colors, danger);
+ $ssh-disconnected: mat-color($primary);
+ $ssh-background: map-get($foreground-colors, base);
+ $ssh-text: map-get($status-colors, text);
+
+ .ssh-viewer {
+
+ .ssh-terminal {
+ background-color: $ssh-background;
+ border: 2px solid $ssh-background;
+ }
+
+ &__error {
+ background-color: $ssh-error;
+ color: $ssh-text;
+ }
+
+ &__disconnected {
+ background-color: $ssh-disconnected;
+ color: $ssh-text;
+ }
+ }
+}
diff --git a/src/app/shared/components/ssh-viewer/ssh-viewer.component.ts b/src/app/shared/components/ssh-viewer/ssh-viewer.component.ts
new file mode 100644
index 0000000000..f6fa1d5a95
--- /dev/null
+++ b/src/app/shared/components/ssh-viewer/ssh-viewer.component.ts
@@ -0,0 +1,133 @@
+import { Component, OnInit, OnDestroy, AfterViewChecked, Input, Output, ViewChild, ElementRef, ViewEncapsulation,
+ EventEmitter, HostListener } from '@angular/core';
+
+import { Observable } from 'rxjs/Rx';
+import { Subscription } from 'rxjs/Subscription';
+import { QueueingSubject } from 'queueing-subject';
+import { Subject } from 'rxjs/Subject';
+
+// Import Xterm
+import * as Terminal from 'xterm/dist/xterm.js';
+import 'xterm/dist/addons/fit/fit.js';
+import { map } from 'rxjs/operators';
+import { ChangeDetectorRef } from '@angular/core';
+@Component({
+ selector: 'app-ssh-viewer',
+ templateUrl: './ssh-viewer.component.html',
+ styleUrls: ['./ssh-viewer.component.scss']
+})
+export class SshViewerComponent implements OnInit, OnDestroy, AfterViewChecked {
+
+ @Input('errorMessage')
+ errorMessage: string;
+
+ @Input('sshStream')
+ sshStream: Observable;
+
+ @Input('sshInput')
+ sshInput: QueueingSubject;
+
+ @Input('connectionStatus')
+ public connectionStatus: Observable;
+
+ public isConnected = false;
+ public isConnecting = false;
+ private isDestroying = false;
+
+ @ViewChild('terminal') container: ElementRef;
+ private xterm: Terminal;
+
+ private msgSubscription: Subscription;
+ private connectSubscription: Subscription;
+
+ private onTermSendData = this.termSendData.bind(this);
+ private onTermResize = this.termResize.bind(this);
+
+ constructor(private changeDetector: ChangeDetectorRef) { }
+
+ ngOnInit() {
+ if (!this.connectionStatus) {
+ return;
+ }
+
+ this.connectSubscription = this.connectionStatus.subscribe((count: number) => {
+ this.isConnected = (count !== 0);
+ if (this.isConnected) {
+ this.xterm.focus();
+ this.isConnecting = false;
+ }
+ if (!this.isDestroying) {
+ this.changeDetector.detectChanges();
+ }
+ });
+
+ this.xterm = new Terminal({
+ cols: 80,
+ rows: 40
+ });
+
+ this.xterm.open(this.container.nativeElement, false);
+ this.xterm.on('data', this.onTermSendData);
+ this.xterm.on('resize', this.onTermResize);
+
+ this.reconnect();
+ }
+
+ ngAfterViewChecked() {
+ if (this.xterm) {
+ this.xterm.fit();
+ }
+ }
+
+ ngOnDestroy() {
+ this.isDestroying = true;
+ this.xterm.off('data', this.onTermSendData);
+ this.xterm.off('resize', this.onTermResize);
+ this.disconnect();
+ this.connectSubscription.unsubscribe();
+ }
+
+ disconnect() {
+ this.isConnecting = false;
+ this.isConnected = false;
+ this.errorMessage = undefined;
+ if (!this.msgSubscription.closed) {
+ this.msgSubscription.unsubscribe();
+ }
+ }
+
+ reconnect() {
+ this.isConnecting = true;
+ this.errorMessage = undefined;
+ this.xterm.reset();
+ this.msgSubscription = this.sshStream
+ .subscribe(
+ (data: string) => {
+ for (const c of data.split(' ')) {
+ this.xterm.write(String.fromCharCode(parseInt(c, 16)));
+ }
+ },
+ (err) => {
+ this.disconnect();
+ },
+ () => {
+ this.disconnect();
+ if (!this.isDestroying) {
+ this.changeDetector.detectChanges();
+ }
+ }
+ );
+ }
+
+ termSendData(d) {
+ if (!this.msgSubscription.closed) {
+ this.sshInput.next(JSON.stringify({key: d}));
+ }
+ }
+
+ termResize(size) {
+ if (!this.msgSubscription.closed && this.sshInput) {
+ this.sshInput.next(JSON.stringify({cols: size.cols, rows: size.rows}));
+ }
+ }
+}
diff --git a/src/app/shared/list-configs/cf-app-instances-config.service.spec.ts b/src/app/shared/list-configs/cf-app-instances-config.service.spec.ts
index 39d567d4b5..4f2333e4ec 100644
--- a/src/app/shared/list-configs/cf-app-instances-config.service.spec.ts
+++ b/src/app/shared/list-configs/cf-app-instances-config.service.spec.ts
@@ -10,6 +10,7 @@ import { ApplicationSchema, GetApplication } from '../../store/actions/applicati
import { CommonModule } from '@angular/common';
import { CoreModule } from '../../core/core.module';
import { ApplicationsModule } from '../../features/applications/applications.module';
+import { RouterTestingModule } from '@angular/router/testing';
describe('CfAppInstancesConfigService', () => {
@@ -33,7 +34,8 @@ describe('CfAppInstancesConfigService', () => {
CoreModule,
SharedModule,
ApplicationsModule,
- createBasicStoreModule()
+ createBasicStoreModule(),
+ RouterTestingModule,
]
});
});
diff --git a/src/app/shared/list-configs/cf-app-instances-config.service.ts b/src/app/shared/list-configs/cf-app-instances-config.service.ts
index d5e9103a94..44a6326f12 100644
--- a/src/app/shared/list-configs/cf-app-instances-config.service.ts
+++ b/src/app/shared/list-configs/cf-app-instances-config.service.ts
@@ -23,6 +23,7 @@ import { Injectable } from '@angular/core';
import { TableCellActionsComponent } from '../components/table/table-cell-actions/table-cell-actions.component';
import { DeleteApplicationInstance } from '../../store/actions/application.actions';
import { AppStat } from '../../store/types/app-metadata.types';
+import { Router } from '@angular/router';
@Injectable()
export class CfAppInstancesConfigService implements IListConfig {
@@ -91,9 +92,15 @@ export class CfAppInstancesConfigService implements IListConfig
enabled: row => !!(row.value && row.value.state === 'RUNNING'),
};
- private listActionSSh: IListAction = {
+
+ private listActionSsh: IListAction = {
action: (item) => {
- window.alert('SSH!');
+ console.log(item);
+ const index = item.index;
+ const sshRoute = (
+ `/applications/${this.appService.cfGuid}/${this.appService.appGuid}/ssh/${index}`
+ );
+ this.router.navigate([sshRoute]);
},
icon: 'computer',
label: 'SSH',
@@ -102,12 +109,19 @@ export class CfAppInstancesConfigService implements IListConfig
enabled: row => !!(row.value && row.value.state === 'RUNNING'),
};
+
+
private singleActions = [
this.listActionTerminate,
- this.listActionSSh
+ this.listActionSsh,
];
- constructor(private store: Store, private appService: ApplicationService, private utilsService: UtilsService) {
+ constructor(
+ private store: Store,
+ private appService: ApplicationService,
+ private utilsService: UtilsService,
+ private router: Router,
+ ) {
this.instancesSource = new CfAppInstancesDataSource(
this.store,
this.appService.cfGuid,
diff --git a/src/app/shared/shared.module.ts b/src/app/shared/shared.module.ts
index 02e6c70f1a..90370c9996 100644
--- a/src/app/shared/shared.module.ts
+++ b/src/app/shared/shared.module.ts
@@ -54,6 +54,7 @@ import {
TableCellEndpointStatusComponent
} from './components/table/custom-cells/table-cell-endpoint-status/table-cell-endpoint-status.component';
import { DialogErrorComponent } from './components/dialog-error/dialog-error.component';
+import { SshViewerComponent } from './components/ssh-viewer/ssh-viewer.component';
import { TableCellAppStatusComponent } from './components/table/custom-cells/table-cell-app-status/table-cell-app-status.component';
import {
ApplicationStateIconComponent,
@@ -80,6 +81,7 @@ import { TableCellUsageComponent } from './components/table/custom-cells/table-c
import { TableCellStatusDirective } from './components/table/table-cell-status.directive';
import { CardAppUsageComponent } from './components/cards/custom-cards/card-app-usage/card-app-usage.component';
+
@NgModule({
imports: [
CommonModule,
@@ -131,6 +133,7 @@ import { CardAppUsageComponent } from './components/cards/custom-cards/card-app-
CardEndpointComponent,
TableCellEndpointStatusComponent,
DialogErrorComponent,
+ SshViewerComponent,
TableCellAppStatusComponent,
ApplicationStateIconPipe,
ApplicationStateIconComponent,
@@ -177,6 +180,7 @@ import { CardAppUsageComponent } from './components/cards/custom-cards/card-app-
NoContentMessageComponent,
EndpointsMissingComponent,
ApplicationStateComponent,
+ SshViewerComponent,
PageSubheaderComponent,
TileComponent,
TileGroupComponent,
diff --git a/src/sass/_all-theme.scss b/src/sass/_all-theme.scss
index c458afeb2b..38d31400ee 100644
--- a/src/sass/_all-theme.scss
+++ b/src/sass/_all-theme.scss
@@ -11,6 +11,7 @@
@import '../app/shared/components/page-subheader/page-subheader.component.theme';
@import '../app/shared/components/card-status/card-status.component.theme';
@import '../app/shared/components/usage-gauge/usage-gauge.component.theme';
+@import '../app/shared/components/ssh-viewer/ssh-viewer.component.theme';
@import './components/mat-tabs.theme';
@import './components/text-status.theme';
@import './mat-themes';
@@ -67,6 +68,7 @@ $side-nav-light-active: #484848;
@include app-text-status-theme($theme, $app-theme);
@include app-card-status-theme($theme, $app-theme);
@include app-usage-gauge-theme($theme, $app-theme);
+ @include app-ssh-viewer-theme($theme, $app-theme);
}
@function app-generate-nav-theme($theme, $nav-theme: null) {
@@ -92,13 +94,15 @@ $side-nav-light-active: #484848;
} @else {
$warn: map-get($theme, warn);
$primary: map-get($theme, primary);
+ $white: #fff;
// Use default palette for status
@return (
success: map-get($mat-green, 500),
warning: map-get($mat-orange, 500),
danger: mat-color($warn),
tentative: map-get($mat-grey, 500),
- busy: mat-color($primary)
+ busy: mat-color($primary),
+ text: $white,
);
}
}