Skip to content
Snippets Groups Projects
browser-window.service.ts 5.67 KiB
/*
 * The browser window service deals with opening a new window
 * once all the tunnels are setup.
 * It also monitors for that window being closed and displays a dialog
 * in case it is (prompting to terminate the process)
 * Temination occurs by setting an rxjs Subject which must be subscribe to
 * by launcher-dialog which will in turn call TES service cancel
 */
import { Injectable } from '@angular/core';
import { Job } from './job';
import { AppAction, Strudelapp, StrudelappInstance } from './strudelapp';
import {BackendSelectionService} from './backend-selection.service';
import {repeat, take, takeUntil, filter, catchError, map, tap} from 'rxjs/operators';
import {timer, interval, Subject, BehaviorSubject, of} from 'rxjs';
import { ModaldialogComponent } from './modaldialog/modaldialog.component';
import { MatDialog, MatDialogRef, MAT_DIALOG_DATA, MatDialogModule } from '@angular/material';
import {NotificationsService } from './notifications.service';
import { HttpClientModule, HttpClient, HttpHeaders, HttpErrorResponse } from     '@angular/common/http';
import { SettingsService } from './settings.service';

import { environment } from '../environments/environment';

@Injectable({
  providedIn: 'root'
})
export class BrowserWindowService {
  private twsproxy: string;
  private Base: string;
  private authdone$: Subject<boolean>;
  private openapps: any[];
  public cancelJob$: Subject<Job>;

  constructor(private backendSelectionService: BackendSelectionService,
    public dialog: MatDialog,
    private notifications: NotificationsService,
    private http: HttpClient,
    private settingsService: SettingsService) { 
    this.backendSelectionService.apiserver.subscribe( (value) => { if (value != null) {this.twsproxy = value.tws ; this.Base = value.tes }});
    this.authdone$ = new Subject<boolean>();
    this.openapps = [];
    this.cancelJob$ = new Subject<Job>();
    timer(500).pipe(repeat()).subscribe(() => this.checkWindows());
    timer(environment.loginterval).pipe(repeat()).subscribe(() => this.logUsage());
  }




 private windowLoaded(window: any, location: string): boolean {
   try {
     if (window.location.toString() == location) {
       return true;
     }
     return false;
   } catch {
     return true;
   }
 }

 public openAppWindow( job: Job, url: any, basicAuth: boolean, action: AppAction, appinst: any ) {
     var re = /^https:\/\/([a-z0-9\.-]+)\/?/;
     let twshost = this.twsproxy.replace(re,"$1");
     let windowloc = url.replace(/\{twsproxy\}/g,this.twsproxy).replace(/twshost/g,twshost);
     var authwindow = null;
     console.log('openAppWindow entered');
   
     if (basicAuth) {
       let authwindowloc = windowloc.replace(/^https:\/\//,'https://'+appinst.username+':'+appinst.password+'@');
       authwindow = window.open(authwindowloc);
       this.authdone$.pipe(take(1)).subscribe( () => { authwindow.close() ; this.finishAppWindow(windowloc,job,action) });
       interval(500).pipe(takeUntil(this.authdone$),filter((v) => this.windowLoaded(authwindow, authwindowloc))).subscribe( () => this.authdone$.next(true));
       return
     } 
     if (action.postData !== undefined && action.postData !== null) {
       let params = {}
       for (let k in action.postData) {
         params[k] = action.postData[k].replace(/\{password\}/g,appinst.password).replace(/\{username\}/g,appinst.username);
       }
       this.openWindowWithPostRequest(windowloc,params);
       return
     }


     this.finishAppWindow(windowloc,job, action);
 }


  public openWindowWithPostRequest(url: string, params: any) {
  var winName='MyWindow';
  var form = document.createElement("form");
  form.setAttribute("method", "post");
  form.setAttribute("action", url);
  form.setAttribute("target",winName);
  for (var i in params) {
    if (params.hasOwnProperty(i)) {
      var input = document.createElement('input');
      input.type = 'hidden';
      input.name = i;
      input.value = params[i];
      form.appendChild(input);
    }
  }
  document.body.appendChild(form);
  window.open('', winName);
  form.target = winName;
  form.submit();
  document.body.removeChild(form);
}

  public logUsage() {
    var app: any;
    if (this.settingsService.logging) {
    this.openapps.forEach( (app,index) => {
      if (!app.window.closed) {
        if (app.job.state == 'RUNNING') {
          this.http.get<any>(environment.logserver+"/"+app.job.identity.site.name+"/"+app.job.identity.username+"/"+app.job.app.name+"/"+app.job.jobid).pipe( //We're expecting 404 not founds
            catchError((e) => {return of([]);})
          ).subscribe((_) => {return})
        }
      }
    })
    }
  }


  public checkWindows() {
    var app: any;
    this.openapps.forEach( (app,index) => {
      if (app.window.closed) {
        if (app.job.state == 'RUNNING') {
          let dialogRef = this.dialog.open(ModaldialogComponent, {
            width: '600px',
            data: app.job,
          });
          dialogRef.afterClosed().subscribe((job) => {if (job !== null) { 
            this.cancelJob$.next(job);
          }});
        }
        this.openapps.splice(index,1);
      }
    })
  }

   public finishAppWindow(windowloc: any, job:Job, action: AppAction) {
     let appwindow = window.open(windowloc);
     if (appwindow == null) {
         this.notifications.notify('It looks like a window failed to open. Please check your popup blocker settings (Strudel 2 needs to be able to open a window to your application');
         return;
     }
     if (appwindow.closed) {
       return
     }
     if (action.name == "Connect") { // actions like viewing logs don't cause us to monitor for the window closing. Only actions like connecting to desktops/jupyter labs
      this.openapps.push({'window':appwindow,'job':job})
     }
 }
}