mirror of
https://github.com/avatao-content/frontend-tutorial-framework
synced 2024-12-05 02:31:33 +00:00
commit
3b94ed0db6
@ -8,6 +8,7 @@ import { DashboardComponent } from './dashboard/dashboard.component';
|
||||
import { IdeComponent } from './ide/ide.component';
|
||||
import { TerminalComponent } from './terminal/terminal.component';
|
||||
import { MessagesComponent } from './messages/messages.component';
|
||||
import { ConsoleComponent } from './console/console.component';
|
||||
import { TestmessengerComponent } from './testmessenger/testmessenger.component';
|
||||
import { config } from './config';
|
||||
|
||||
@ -17,6 +18,7 @@ const routes: Routes = [
|
||||
{ path: config.ide.route, component: IdeComponent },
|
||||
{ path: config.terminal.route, component: TerminalComponent },
|
||||
{ path: config.messages.route, component: MessagesComponent },
|
||||
{ path: config.console.route, component: ConsoleComponent },
|
||||
{ path: config.testmessenger.route, component: TestmessengerComponent }
|
||||
];
|
||||
|
||||
|
@ -26,6 +26,8 @@ import { AppRoutingModule } from './app-routing.module';
|
||||
import { TestmessengerComponent } from './testmessenger/testmessenger.component';
|
||||
import { DeploymentNotificationService } from './services/deployment-notification.service';
|
||||
import { SafePipe } from './pipes/safe.pipe';
|
||||
import { ConsoleComponent } from './console/console.component';
|
||||
import { ProcessLogService } from './services/processlog.service';
|
||||
|
||||
|
||||
@NgModule({
|
||||
@ -39,7 +41,8 @@ import { SafePipe } from './pipes/safe.pipe';
|
||||
TerminalComponent,
|
||||
DashboardComponent,
|
||||
TestmessengerComponent,
|
||||
SafePipe
|
||||
SafePipe,
|
||||
ConsoleComponent
|
||||
],
|
||||
imports: [
|
||||
BrowserModule,
|
||||
@ -55,7 +58,8 @@ import { SafePipe } from './pipes/safe.pipe';
|
||||
TerminadoService,
|
||||
FSMUpdateService,
|
||||
ProcessManagerService,
|
||||
DeploymentNotificationService
|
||||
DeploymentNotificationService,
|
||||
ProcessLogService
|
||||
],
|
||||
bootstrap: [
|
||||
AppComponent
|
||||
|
@ -5,6 +5,7 @@ export const config = {
|
||||
documentTitle: 'Avatao Tutorials',
|
||||
dashboard: {
|
||||
route: 'dashboard',
|
||||
terminalOrConsole: 'terminal',
|
||||
currentLayout: 'terminal-ide-web',
|
||||
enabledLayouts: [
|
||||
'terminal-ide-web',
|
||||
@ -26,14 +27,25 @@ export const config = {
|
||||
defaultLanguage: 'text',
|
||||
deployProcessName: 'webservice',
|
||||
showDeployButton: true,
|
||||
reloadIframeOnDeployButtonClick: true
|
||||
reloadIframeOnDeploy: true,
|
||||
showConsoleOnDeploy: true,
|
||||
},
|
||||
terminal: {
|
||||
route: 'shell'
|
||||
},
|
||||
messages: {
|
||||
route: 'messages',
|
||||
showNextButton: true
|
||||
showNextButton: false
|
||||
},
|
||||
console: {
|
||||
route: 'console',
|
||||
defaultContent: '',
|
||||
rewriteContentWithProcessLogsOnDeploy: 'stdout',
|
||||
showLiveLogs: true,
|
||||
defaultLogs: {
|
||||
stdout: '',
|
||||
stderr: ''
|
||||
}
|
||||
},
|
||||
testmessenger: {
|
||||
route: 'testmessenger'
|
||||
|
9
src/app/console/console-command.ts
Normal file
9
src/app/console/console-command.ts
Normal file
@ -0,0 +1,9 @@
|
||||
// Copyright (C) 2018 Avatao.com Innovative Learning Kft.
|
||||
// All Rights Reserved. See LICENSE file for details.
|
||||
|
||||
export interface ConsoleCommand {
|
||||
command: string;
|
||||
content?: string;
|
||||
showLiveLogs?: boolean;
|
||||
rewriteContentWithProcessLogsOnDeploy?: string;
|
||||
}
|
10
src/app/console/console.component.html
Normal file
10
src/app/console/console.component.html
Normal file
@ -0,0 +1,10 @@
|
||||
<!-- Copyright (C) 2018 Avatao.com Innovative Learning Kft.
|
||||
All Rights Reserved. See LICENSE file for details. -->
|
||||
|
||||
<textarea [(ngModel)]="console_content"
|
||||
#tfwconsole
|
||||
[scrollTop]="tfwconsole.scrollHeight"
|
||||
readonly
|
||||
class="tfw-console"
|
||||
spellcheck="false">
|
||||
</textarea>
|
15
src/app/console/console.component.scss
Normal file
15
src/app/console/console.component.scss
Normal file
@ -0,0 +1,15 @@
|
||||
// Copyright (C) 2018 Avatao.com Innovative Learning Kft.
|
||||
// All Rights Reserved. See LICENSE file for details.
|
||||
|
||||
@import "../../assets/scss/variables.scss";
|
||||
|
||||
.tfw-console {
|
||||
resize: none;
|
||||
height: 100%;
|
||||
width: 100%;
|
||||
background-color: $tao-gray-800;
|
||||
border: 1px solid $tao-gray-800;
|
||||
color: white;
|
||||
padding: 0 8px 8px 8px;
|
||||
font-size: small;
|
||||
}
|
73
src/app/console/console.component.ts
Normal file
73
src/app/console/console.component.ts
Normal file
@ -0,0 +1,73 @@
|
||||
// Copyright (C) 2018 Avatao.com Innovative Learning Kft.
|
||||
// All Rights Reserved. See LICENSE file for details.
|
||||
|
||||
import { Component, OnInit } from '@angular/core';
|
||||
import { WebSocketService } from '../services/websocket.service';
|
||||
import { ConsoleCommand } from './console-command';
|
||||
import { config } from '../config';
|
||||
import { ProcessLogService } from '../services/processlog.service';
|
||||
import { LogMessage } from '../services/log.message';
|
||||
|
||||
@Component({
|
||||
selector: 'app-console',
|
||||
templateUrl: './console.component.html',
|
||||
styleUrls: ['./console.component.scss']
|
||||
})
|
||||
export class ConsoleComponent implements OnInit {
|
||||
console_content: string = config.console.defaultContent;
|
||||
rewriteContentWithProcessLogsOnDeploy: string = config.console.rewriteContentWithProcessLogsOnDeploy;
|
||||
|
||||
command_handlers = {
|
||||
'write': this.writeHandler.bind(this),
|
||||
'read': this.readHandler.bind(this),
|
||||
'showLiveLogs': this.showLiveLogsHandler.bind(this),
|
||||
'rewriteContentWithProcessLogsOnDeploy': this.rewriteContentWithProcessLogsOnDeployHandler.bind(this)
|
||||
};
|
||||
|
||||
constructor(private webSocketService: WebSocketService,
|
||||
private processLogService: ProcessLogService) {}
|
||||
|
||||
ngOnInit() {
|
||||
this.webSocketService.connect();
|
||||
this.webSocketService.observeKey<ConsoleCommand>('console').subscribe(
|
||||
(event) => this.command_handlers[event.data.command](event.data)
|
||||
);
|
||||
this.processLogService.newLogs.subscribe((data) => this.newLogsHandler(data));
|
||||
}
|
||||
|
||||
writeHandler(data: ConsoleCommand) {
|
||||
this.setContent(data.content);
|
||||
}
|
||||
|
||||
readHandler(data: ConsoleCommand) {
|
||||
this.sendContent(this.console_content);
|
||||
}
|
||||
|
||||
newLogsHandler(logs: LogMessage) {
|
||||
if (this.rewriteContentWithProcessLogsOnDeploy !== '') {
|
||||
const log = logs[this.rewriteContentWithProcessLogsOnDeploy];
|
||||
if (log) {
|
||||
this.setContent(log);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
showLiveLogsHandler(data: ConsoleCommand) {
|
||||
this.processLogService.showLiveLogs = data.showLiveLogs;
|
||||
}
|
||||
|
||||
rewriteContentWithProcessLogsOnDeployHandler(data: ConsoleCommand) {
|
||||
this.rewriteContentWithProcessLogsOnDeploy = data.rewriteContentWithProcessLogsOnDeploy;
|
||||
}
|
||||
|
||||
setContent(content: string) {
|
||||
this.console_content = content;
|
||||
}
|
||||
|
||||
sendContent(content: string) {
|
||||
this.webSocketService.send('console', {
|
||||
'command': 'read',
|
||||
'content': content
|
||||
});
|
||||
}
|
||||
}
|
@ -4,9 +4,11 @@
|
||||
<div [attr.class]="layout">
|
||||
<div class="tfw-grid-main-components">
|
||||
<div class="tfw-header"><app-header></app-header></div>
|
||||
<div [ngClass]="{ 'hide-attribute': hide_messages }" class="tfw-messages"><app-messages></app-messages></div>
|
||||
<div [ngClass]="{'hide-attribute': hide_messages}" class="tfw-messages">
|
||||
<app-messages></app-messages>
|
||||
</div>
|
||||
<div class="tfw-web tao-grid-top-left"
|
||||
[ngClass]="{ 'deploy-blur': deploying }">
|
||||
[ngClass]="{'deploy-blur': deploying}">
|
||||
<app-web *ngIf="!iframeUrl"></app-web>
|
||||
<div *ngIf="iframeUrl" class="iframe-container">
|
||||
<iframe class="iframe"
|
||||
@ -18,10 +20,20 @@
|
||||
</div>
|
||||
</div>
|
||||
<div class="tfw-ide">
|
||||
<app-ide></app-ide>
|
||||
<app-ide (newLogs)="setConsoleContentIfNoLiveLogs($event)"></app-ide>
|
||||
</div>
|
||||
<div class="tfw-terminal">
|
||||
<app-terminal></app-terminal>
|
||||
<div class="btn-group btn-group-sm flex-wrap tao-grid-center-left tfw-console-terminal-menu">
|
||||
<button class="tfw-console-terminal-menu-button"
|
||||
(click)="selectTerminalMenuItem('terminal')"
|
||||
[class.selected]="selectedTerminalMenuItem === 'terminal'">TERMINAL</button>
|
||||
<button class="tfw-console-terminal-menu-button"
|
||||
(click)="selectTerminalMenuItem('console')"
|
||||
[class.selected]="selectedTerminalMenuItem === 'console'">CONSOLE</button>
|
||||
</div>
|
||||
<hr>
|
||||
<app-terminal [hidden]="selectedTerminalMenuItem !== 'terminal'"></app-terminal>
|
||||
<app-console [hidden]="selectedTerminalMenuItem !== 'console'"></app-console>
|
||||
</div>
|
||||
<div class="tfw-sidebar">
|
||||
<app-sidebar (layoutChanged)="setLayout($event)" [layout]="layout"></app-sidebar>
|
||||
|
@ -76,11 +76,34 @@
|
||||
.tfw-terminal {
|
||||
overflow-y: hidden;
|
||||
background-color: $tao-gray-800;
|
||||
padding-bottom: 2.2em;
|
||||
|
||||
div[class*="web"] & {
|
||||
border-top: 1px solid $tao-plum-100;
|
||||
}
|
||||
}
|
||||
|
||||
hr {
|
||||
display: block;
|
||||
height: 1px;
|
||||
border: 0;
|
||||
border-top: 1px solid rgba(255, 255, 255, 0.15);
|
||||
margin: 2px 0 7px 0;
|
||||
padding: 0;
|
||||
}
|
||||
|
||||
.tfw-console-terminal-menu-button {
|
||||
font-size: $font-size-small;
|
||||
background-color: transparent;
|
||||
border: none;
|
||||
color: gray;
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
.selected {
|
||||
color: white;
|
||||
cursor: default;
|
||||
}
|
||||
}
|
||||
|
||||
.deploy-blur {
|
||||
|
@ -7,6 +7,8 @@ import { Subscription } from 'rxjs';
|
||||
import { WebSocketService } from '../services/websocket.service';
|
||||
import { LayoutCommand } from './layout-command';
|
||||
import { config } from '../config';
|
||||
import { ProcessLogService } from '../services/processlog.service';
|
||||
import { LogMessage } from '../services/log.message';
|
||||
|
||||
@Component({
|
||||
selector: 'app-dashboard',
|
||||
@ -20,12 +22,17 @@ export class DashboardComponent implements OnInit, OnDestroy {
|
||||
hide_messages: boolean = config.dashboard.hide_messages;
|
||||
iframeUrl: string = config.dashboard.iframeUrl;
|
||||
@ViewChild('webiframe') webiframe: ElementRef;
|
||||
selectedTerminalMenuItem = config.dashboard.terminalOrConsole;
|
||||
|
||||
command_handlers = {'layout': this.layoutHandler.bind(this),
|
||||
'hide_messages': this.hideMessagesHandler.bind(this),
|
||||
'terminal_menu': this.terminalMenuSelectHandler.bind(this),
|
||||
'reload_frontend': this.reloadFrontendHandlder.bind(this)};
|
||||
|
||||
constructor(private deploymentNotificationService: DeploymentNotificationService,
|
||||
private webSocketService: WebSocketService,
|
||||
private changeDetectorRef: ChangeDetectorRef) {}
|
||||
private changeDetectorRef: ChangeDetectorRef,
|
||||
private processLogService: ProcessLogService) {}
|
||||
|
||||
ngOnInit() {
|
||||
this.webSocketService.connect();
|
||||
@ -44,7 +51,7 @@ export class DashboardComponent implements OnInit, OnDestroy {
|
||||
this.deploymentNotificationSubscription = this.deploymentNotificationService.deploying.subscribe(
|
||||
(deploying) => {
|
||||
this.deploying = deploying;
|
||||
if (!deploying && config.ide.reloadIframeOnDeployButtonClick) {
|
||||
if (!deploying && config.ide.reloadIframeOnDeploy) {
|
||||
this.reloadIframe();
|
||||
}
|
||||
});
|
||||
@ -56,9 +63,14 @@ export class DashboardComponent implements OnInit, OnDestroy {
|
||||
} else {
|
||||
console.log('Invalid ide layout "' + data.layout + '" received!');
|
||||
}
|
||||
if (data.hide_messages !== undefined) {
|
||||
this.hide_messages = data.hide_messages;
|
||||
}
|
||||
}
|
||||
|
||||
hideMessagesHandler(data: LayoutCommand) {
|
||||
this.hide_messages = data.hide_messages;
|
||||
}
|
||||
|
||||
terminalMenuSelectHandler(data: LayoutCommand) {
|
||||
this.selectTerminalMenuItem(data.terminal_menu_item);
|
||||
}
|
||||
|
||||
reloadFrontendHandlder(data: LayoutCommand) {
|
||||
@ -83,4 +95,18 @@ export class DashboardComponent implements OnInit, OnDestroy {
|
||||
this.webiframe.nativeElement.contentWindow.location.reload(true);
|
||||
});
|
||||
}
|
||||
|
||||
selectTerminalMenuItem(item: string) {
|
||||
if (!item.match('(terminal|console)')) {
|
||||
return;
|
||||
}
|
||||
this.selectedTerminalMenuItem = item;
|
||||
}
|
||||
|
||||
setConsoleContentIfNoLiveLogs(logs: LogMessage) {
|
||||
this.processLogService.emitNewLogsIfNoLiveLogs(logs);
|
||||
if (config.ide.showConsoleOnDeploy) {
|
||||
this.selectTerminalMenuItem('console');
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -1,8 +1,9 @@
|
||||
// Copyright (C) 2018 Avatao.com Innovative Learning Kft.
|
||||
// All Rights Reserved. See LICENSE file for details.
|
||||
|
||||
export class LayoutCommand {
|
||||
command: string;
|
||||
layout: string;
|
||||
hide_messages?: boolean;
|
||||
export interface LayoutCommand {
|
||||
command: string;
|
||||
layout?: string;
|
||||
hide_messages?: boolean;
|
||||
terminal_menu_item?: string;
|
||||
}
|
||||
|
@ -51,6 +51,7 @@
|
||||
padding: 6px 19px;
|
||||
|
||||
img {
|
||||
padding-right: 0.5em;
|
||||
position: relative;
|
||||
bottom: 1px;
|
||||
height: $small;
|
||||
|
@ -1,7 +1,7 @@
|
||||
// Copyright (C) 2018 Avatao.com Innovative Learning Kft.
|
||||
// All Rights Reserved. See LICENSE file for details.
|
||||
|
||||
import { ChangeDetectorRef, Component, OnInit } from '@angular/core';
|
||||
import { ChangeDetectorRef, Component, EventEmitter, OnInit, Output } from '@angular/core';
|
||||
|
||||
import * as brace from 'brace';
|
||||
import 'brace/ext/modelist';
|
||||
@ -62,6 +62,9 @@ export class IdeComponent implements OnInit {
|
||||
|
||||
language: string = config.ide.defaultLanguage;
|
||||
theme = 'cobalt';
|
||||
|
||||
@Output() newLogs = new EventEmitter<any>();
|
||||
|
||||
options: any = {enableBasicAutocompletion: true,
|
||||
enableSnippets: true,
|
||||
enableLiveAutocompletion: true};
|
||||
@ -96,7 +99,13 @@ export class IdeComponent implements OnInit {
|
||||
this.processManagerService.init();
|
||||
this.processManagerService.subscribeCallback(
|
||||
config.ide.deployProcessName,
|
||||
(event) => this.deploymentNotificationService.deploying.next(false)
|
||||
(event) => {
|
||||
this.deploymentNotificationService.deploying.next(false);
|
||||
this.newLogs.emit({
|
||||
stdout: event.data.stdout,
|
||||
stderr: event.data.stderr
|
||||
});
|
||||
}
|
||||
);
|
||||
|
||||
this.processManagerService.subscribeSuccessCallback(
|
||||
|
7
src/app/services/log.message.ts
Normal file
7
src/app/services/log.message.ts
Normal file
@ -0,0 +1,7 @@
|
||||
// Copyright (C) 2018 Avatao.com Innovative Learning Kft.
|
||||
// All Rights Reserved. See LICENSE file for details.
|
||||
|
||||
export interface LogMessage {
|
||||
stdout: string;
|
||||
stderr: string;
|
||||
}
|
@ -1,8 +1,10 @@
|
||||
// Copyright (C) 2018 Avatao.com Innovative Learning Kft.
|
||||
// All Rights Reserved. See LICENSE file for details.
|
||||
|
||||
export class ProcessCommand {
|
||||
export interface ProcessCommand {
|
||||
command: string;
|
||||
process_name: string;
|
||||
error?: string;
|
||||
stdout: string;
|
||||
stderr: string;
|
||||
}
|
||||
|
8
src/app/services/processlog-command.ts
Normal file
8
src/app/services/processlog-command.ts
Normal file
@ -0,0 +1,8 @@
|
||||
// Copyright (C) 2018 Avatao.com Innovative Learning Kft.
|
||||
// All Rights Reserved. See LICENSE file for details.
|
||||
|
||||
export interface ProcessLogCommand {
|
||||
command: string;
|
||||
stdout: string;
|
||||
stderr: string;
|
||||
}
|
41
src/app/services/processlog.service.ts
Normal file
41
src/app/services/processlog.service.ts
Normal file
@ -0,0 +1,41 @@
|
||||
// Copyright (C) 2018 Avatao.com Innovative Learning Kft.
|
||||
// All Rights Reserved. See LICENSE file for details.
|
||||
|
||||
import { Injectable } from '@angular/core';
|
||||
import { WebSocketService } from './websocket.service';
|
||||
import { ProcessLogCommand } from './processlog-command';
|
||||
import { config } from '../config';
|
||||
import { BehaviorSubject } from 'rxjs/BehaviorSubject';
|
||||
import { LogMessage } from './log.message';
|
||||
|
||||
@Injectable()
|
||||
export class ProcessLogService {
|
||||
newLogs = new BehaviorSubject<LogMessage>(config.console.defaultLogs);
|
||||
showLiveLogs = config.console.showLiveLogs;
|
||||
|
||||
command_handlers = {
|
||||
'new_log': this.newLogsHandler.bind(this)
|
||||
};
|
||||
|
||||
constructor(private webSocketService: WebSocketService) {
|
||||
this.webSocketService.connect();
|
||||
this.webSocketService.observeKey<ProcessLogCommand>('processlog').subscribe(
|
||||
(event) => this.command_handlers[event.data.command](event.data)
|
||||
);
|
||||
}
|
||||
|
||||
emitNewLogsIfNoLiveLogs(logs: LogMessage) {
|
||||
if (!this.showLiveLogs) {
|
||||
this.newLogs.next(logs);
|
||||
}
|
||||
}
|
||||
|
||||
newLogsHandler(data: ProcessLogCommand) {
|
||||
if (this.showLiveLogs) {
|
||||
this.newLogs.next({
|
||||
stdout: data.stdout,
|
||||
stderr: data.stderr
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
Loading…
Reference in New Issue
Block a user