import {of as observableOf, Observable} from 'rxjs'; import {catchError, map} from 'rxjs/operators'; import {Injectable} from '@angular/core'; import {HttpClient, HttpErrorResponse} from '@angular/common/http'; import {CookieService} from 'ngx-cookie-service'; import {MatTableDataSource} from '@angular/material'; import {SnackBarService} from './snackbar.service'; import {StompConfig, StompRService} from '@stomp/ng2-stompjs'; import {Message} from '@stomp/stompjs'; import {environment} from '../environments/environment'; import {IdP, User, Service, AuthInfo, AllAuthInfo, UserState, SSHKey, NewSSHKey} from './types/types.module'; @Injectable() export class UserService { public loggedIn = false; public user: User; public sshKeyData: MatTableDataSource; public userInfoData: MatTableDataSource; public services: Service[]; public messages: string[]; constructor( public cookieService: CookieService, public http: HttpClient, public snackBar: SnackBarService, private _stompService: StompRService, ) { this.update(); this.messages = []; } public setIdPPreference(idp: IdP) { this.cookieService.set(environment.idpCookieName, String(idp.id)); } public errorHandler(error: any): void { if (error.status === 500) { this.snackBar.open('Server Error'); } } public getIdPPreference(): Observable { let idpID = Number(this.cookieService.get(environment.idpCookieName)); return this.http.get('/backend/auth/v1/info/').pipe(map( (authInfo: AuthInfo) => { let selected = authInfo.idps[1]; if (!idpID) { idpID = authInfo.default; } for (const idp of authInfo.idps) { if (idp.id === idpID) { selected = idp; } } return { idps: authInfo.idps, selected: selected, }; } ),catchError( (error: any) => { this.errorHandler(error); return observableOf(null); } ),); } private _updateUserValues(newUser: any) { this.user = newUser; if (this.user) { // build sshKeyData if ('ssh_keys' in this.user) { this.sshKeyData = new MatTableDataSource(this.user.ssh_keys); } else { this.sshKeyData = undefined; } // build userInfoData const userInfoList = []; for (const key in this.user.userinfo) { if (this.user.userinfo.hasOwnProperty(key)) { userInfoList.push({name: key, info: this.user.userinfo[key]}); } } this.userInfoData = new MatTableDataSource(userInfoList); } else { this.sshKeyData = undefined; this.userInfoData = undefined; } } public updateData(newData: any) { // did a login occur? let login = (!this.loggedIn && newData.logged_in); // did a logout occur? let logout = (this.loggedIn && !newData.logged_in); // report an occured error if (newData.error) { this.snackBar.open(newData.error); } // -- Value updating -- this.loggedIn = newData.logged_in; // this check is needed because the accordeon collapses if we overwrite the list if (!this.services) { this.services = newData.services; } this._updateUserValues(newData.user); if (login) { this.snackBar.open('Logged in'); this.connectLiveUpdates() } if (logout) { this.snackBar.open('Logged out'); this._stompService.disconnect(); // reset some attributes this.services = []; } } public updateState() { this.http .get('/backend/api/state/') .subscribe( (data: UserState) => { this.updateData(data); }, (err: HttpErrorResponse) => { console.log('Error', err); this.snackBar.open('Error receiving data from the server'); } ); } public update() { this.updateState(); } private initStomp() { // handle with care let login = this.user.id let passcode = this.cookieService.get('sessionid'); const stompConfig: StompConfig = { // Which server? url: 'wss://hdf-portal.data.kit.edu/ws', // Headers // Typical keys: login, passcode, host headers: { login: 'webpage-client:' + login, passcode: passcode, }, // How often to heartbeat? // Interval in milliseconds, set to 0 to disable heartbeat_in: 0, // Typical value 0 - disabled heartbeat_out: 20000, // Typical value 20000 - every 20 seconds // Wait in milliseconds before attempting auto reconnect // Set to 0 to disable // Typical value 5000 (5 seconds) reconnect_delay: 5000, // Will log diagnostics on console debug: false, }; this._stompService.config = stompConfig; this._stompService.initAndConnect(); } private connectLiveUpdates() { this.initStomp(); let subscription = this._stompService.subscribe( '/exchange/update/' + this.user.id.toString() ); subscription.pipe(map((message: Message) => { return message.body; })).subscribe((body: any) => { let json = JSON.parse(body); if (json.message && json.message != '') { this.snackBar.open(json.message); this.messages.push(json.message); } if (json.user_state) { this._updateUserValues(json.user_state); } }); } public login(idp: IdP) { this.setIdPPreference(idp); window.location.href = 'https://hdf-portal.data.kit.edu/backend/auth/v1/request/'; } public logout() { this.http.post('/backend/auth/v1/logout/', {}).subscribe( data => { this.updateData(data); } ); } public addSshKey(key: NewSSHKey) { const body = { 'type': 'add', 'key': key, }; return this.http.post('/backend/api/sshkey/', body).subscribe( data => { this.updateData(data); }, (err) => { this.snackBar.open('Error uploading key'); console.log(err); this.update(); } ); } public removeSshKey(key: SSHKey) { const body = { 'type': 'remove', 'id': key.id, }; return this.http.post('/backend/api/sshkey/', body).subscribe( (data) => { this.updateData(data); }, (err) => { this.snackBar.open('Error deleting key'); console.log(err); this.update(); } ); } public addDeployment(service: Service, key: SSHKey) { const body = { 'type': 'add', 'key': key.id, 'service': service.id, }; return this.http.post('/backend/api/deployments/', body).subscribe( (data) => { //this.snackBar.open('Deployed key ' + key.name); this.updateData(data); }, (err) => { this.snackBar.open('Error deploying key ' + key.name); console.log(err); this.update(); } ); } public removeDeployment(service: Service, key: SSHKey) { const body = { 'type': 'remove', 'key': key.id, 'service': service.id, }; return this.http.post('/backend/api/deployments/', body).subscribe( (data) => { //this.snackBar.open('Withdrew key ' + key.name); this.updateData(data); }, (err) => { this.snackBar.open('Error withdrawing key ' + key.name); console.log(err); this.update(); } ); } public deleteUser() { return this.http.delete('/backend/api/delete_user/').subscribe( (data) => { this.snackBar.open('Deleted user from server'); this.updateData(data); }, (err) => { this.snackBar.open('Error deleting user from server'); console.log(err); this.update(); } ); } public taskInProgress(site, service): boolean { if (site && service) { for (const item of this.user.deployment_task_items){ if (item.site.id == site.id && item.service.id == service.id) { return true; } } } return false; } }