mirror of
				https://github.com/avatao-content/frontend-tutorial-framework
				synced 2025-11-04 12:22:54 +00:00 
			
		
		
		
	Reconnect websockets automatically
This commit is contained in:
		@@ -5,7 +5,8 @@
 | 
				
			|||||||
  "scripts": {
 | 
					  "scripts": {
 | 
				
			||||||
    "ng": "ng",
 | 
					    "ng": "ng",
 | 
				
			||||||
    "start": "ng serve --proxy-config proxy.conf.json",
 | 
					    "start": "ng serve --proxy-config proxy.conf.json",
 | 
				
			||||||
    "build": "ng build --prod --aot --build-optimizer"
 | 
					    "build": "ng build --prod --aot --build-optimizer",
 | 
				
			||||||
 | 
					    "lint": "ng lint --fix"
 | 
				
			||||||
  },
 | 
					  },
 | 
				
			||||||
  "private": true,
 | 
					  "private": true,
 | 
				
			||||||
  "dependencies": {
 | 
					  "dependencies": {
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -8,11 +8,16 @@ export class TerminadoService {
 | 
				
			|||||||
  xterm: Terminal;
 | 
					  xterm: Terminal;
 | 
				
			||||||
  ws: WebSocket;
 | 
					  ws: WebSocket;
 | 
				
			||||||
  attached = false;
 | 
					  attached = false;
 | 
				
			||||||
 | 
					  private dataListener: any;
 | 
				
			||||||
 | 
					  private resizeListener: any;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  constructor() {
 | 
					  constructor() {
 | 
				
			||||||
    Terminal.applyAddon(fit);
 | 
					    Terminal.applyAddon(fit);
 | 
				
			||||||
    Terminal.applyAddon(terminado);
 | 
					    this.xterm = this.createTerminal();
 | 
				
			||||||
    this.xterm = new Terminal({
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  createTerminal() {
 | 
				
			||||||
 | 
					    return new Terminal({
 | 
				
			||||||
      theme: {
 | 
					      theme: {
 | 
				
			||||||
        foreground: '#ffffff',
 | 
					        foreground: '#ffffff',
 | 
				
			||||||
        background: '#0C0C0C',                  // $tao-gray-800
 | 
					        background: '#0C0C0C',                  // $tao-gray-800
 | 
				
			||||||
@@ -37,26 +42,52 @@ export class TerminadoService {
 | 
				
			|||||||
      },
 | 
					      },
 | 
				
			||||||
      fontSize: 14
 | 
					      fontSize: 14
 | 
				
			||||||
    });
 | 
					    });
 | 
				
			||||||
 | 
					 | 
				
			||||||
    const wsproto = (location.protocol === 'https:') ? 'wss://' : 'ws://';
 | 
					 | 
				
			||||||
    this.ws = new WebSocket(wsproto + window.location.host + '/terminal');
 | 
					 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  attach(element: HTMLElement) {
 | 
					  attach(element: HTMLElement) {
 | 
				
			||||||
 | 
					    if (this.attached) {
 | 
				
			||||||
 | 
					      return;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    const wsproto = (location.protocol === 'https:') ? 'wss://' : 'ws://';
 | 
				
			||||||
 | 
					    this.ws = new WebSocket(wsproto + window.location.host + '/terminal');
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    this.ws.onopen = () => {
 | 
					    this.ws.onopen = () => {
 | 
				
			||||||
      (<any>this.xterm).terminadoAttach(this.ws);
 | 
					      this.attached = true;
 | 
				
			||||||
 | 
					      this.xterm = this.createTerminal();
 | 
				
			||||||
      this.xterm.open(element);
 | 
					      this.xterm.open(element);
 | 
				
			||||||
      this.fit();
 | 
					      this.fit();
 | 
				
			||||||
      this.xterm.blur();
 | 
					      // In order to reset the terminal state after a broken socket, we need to register the listeners manually.
 | 
				
			||||||
      this.attached = true;
 | 
					      (<any>this.xterm)._core.register(this.dataListener = this.xterm.onData(data => {
 | 
				
			||||||
 | 
					        this.ws.send(JSON.stringify(['stdin', data]));
 | 
				
			||||||
 | 
					      }));
 | 
				
			||||||
 | 
					      (<any>this.xterm)._core.register(this.resizeListener = this.xterm.onResize((size: { rows: number, cols: number }) => {
 | 
				
			||||||
 | 
					        this.ws.send(JSON.stringify(['set_size', size.rows, size.cols]));
 | 
				
			||||||
 | 
					      }));
 | 
				
			||||||
 | 
					    };
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    this.ws.onclose = () => {
 | 
				
			||||||
 | 
					      this.detach();
 | 
				
			||||||
 | 
					      this.xterm.destroy();
 | 
				
			||||||
 | 
					      this.attach(element);
 | 
				
			||||||
 | 
					    };
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    this.ws.onmessage = msg => {
 | 
				
			||||||
 | 
					      const data = JSON.parse(msg.data);
 | 
				
			||||||
 | 
					      if (data[0] === 'stdout') {
 | 
				
			||||||
 | 
					        this.xterm.write(data[1]);
 | 
				
			||||||
 | 
					      }
 | 
				
			||||||
    };
 | 
					    };
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  detach() {
 | 
					  detach() {
 | 
				
			||||||
    (<any>this.xterm).terminadoDetach(this.ws);
 | 
					    if (!this.attached) {
 | 
				
			||||||
    this.xterm.destroy();
 | 
					      return;
 | 
				
			||||||
    this.ws.close();
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    this.attached = false;
 | 
					    this.attached = false;
 | 
				
			||||||
 | 
					    this.dataListener.dispose();
 | 
				
			||||||
 | 
					    this.resizeListener.dispose();
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  fit() {
 | 
					  fit() {
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -1,5 +1,5 @@
 | 
				
			|||||||
import { Injectable } from '@angular/core';
 | 
					import { Injectable } from '@angular/core';
 | 
				
			||||||
import { Observable } from 'rxjs';
 | 
					import { Observable, Subject, Subscription } from 'rxjs';
 | 
				
			||||||
import { webSocket, WebSocketSubject } from 'rxjs/webSocket';
 | 
					import { webSocket, WebSocketSubject } from 'rxjs/webSocket';
 | 
				
			||||||
import { filter, map } from 'rxjs/operators';
 | 
					import { filter, map } from 'rxjs/operators';
 | 
				
			||||||
import { WebSocketMessage } from '../message-types/websocket-message';
 | 
					import { WebSocketMessage } from '../message-types/websocket-message';
 | 
				
			||||||
@@ -17,14 +17,28 @@ export enum Intent {
 | 
				
			|||||||
@Injectable()
 | 
					@Injectable()
 | 
				
			||||||
export class WebSocketService {
 | 
					export class WebSocketService {
 | 
				
			||||||
  private ws: WebSocketSubject<WebSocketMessage>;
 | 
					  private ws: WebSocketSubject<WebSocketMessage>;
 | 
				
			||||||
 | 
					  private subject: Subject<WebSocketMessage> = new Subject<WebSocketMessage>();
 | 
				
			||||||
 | 
					  private subscription: Subscription;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  constructor() {}
 | 
					  constructor() {}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  public connect() {
 | 
					  public connect() {
 | 
				
			||||||
    if (!this.ws) {
 | 
					    if (!this.ws) {
 | 
				
			||||||
 | 
					      if (this.subscription) {
 | 
				
			||||||
 | 
					        this.subscription.unsubscribe();
 | 
				
			||||||
 | 
					      }
 | 
				
			||||||
      const wsproto = (location.protocol === 'https:') ? 'wss://' : 'ws://';
 | 
					      const wsproto = (location.protocol === 'https:') ? 'wss://' : 'ws://';
 | 
				
			||||||
      const connAddr = wsproto + window.location.host + '/ws';
 | 
					      const connAddr = wsproto + window.location.host + '/ws';
 | 
				
			||||||
      this.ws = webSocket<WebSocketMessage>(connAddr);
 | 
					      this.ws = webSocket<WebSocketMessage>({
 | 
				
			||||||
 | 
					        url: connAddr,
 | 
				
			||||||
 | 
					        closeObserver: {
 | 
				
			||||||
 | 
					            next: closeEvent => {
 | 
				
			||||||
 | 
					                this.ws = null;
 | 
				
			||||||
 | 
					                this.connect();
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					          }
 | 
				
			||||||
 | 
					      });
 | 
				
			||||||
 | 
					      this.subscription = this.ws.subscribe(msg => this.subject.next(msg));
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@@ -35,7 +49,7 @@ export class WebSocketService {
 | 
				
			|||||||
  }
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  public observeAll<T extends WebSocketMessage>(key: string): Observable<T> {
 | 
					  public observeAll<T extends WebSocketMessage>(key: string): Observable<T> {
 | 
				
			||||||
    return this.ws.pipe(
 | 
					    return this.subject.pipe(
 | 
				
			||||||
      filter(message => message.key.startsWith(key)),
 | 
					      filter(message => message.key.startsWith(key)),
 | 
				
			||||||
      map(message => <T> message)
 | 
					      map(message => <T> message)
 | 
				
			||||||
    );
 | 
					    );
 | 
				
			||||||
 
 | 
				
			|||||||
		Reference in New Issue
	
	Block a user