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, ); } }